From 0ec82c104ded01a44ed36d20e25220fa41887fd0 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 27 Feb 2015 12:18:34 +0100 Subject: Add louloulibs as a submodule --- louloulibs | 1 + 1 file changed, 1 insertion(+) create mode 160000 louloulibs (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs new file mode 160000 index 0000000..b6af145 --- /dev/null +++ b/louloulibs @@ -0,0 +1 @@ +Subproject commit b6af145bfb9561a1bb1ecb940f50163c5ce4dbbb -- cgit v1.2.3 From e6569a1090be063f34624474f0d4578f37a169ae Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 27 Feb 2015 12:40:50 +0100 Subject: Only use include_directory() if the directory path is defined --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index b6af145..d6a3724 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit b6af145bfb9561a1bb1ecb940f50163c5ce4dbbb +Subproject commit d6a3724c6a0127a49a9e7adb1090bb7438c8d0f2 -- cgit v1.2.3 From e4c696861d86b62305ca0ec8136e79f147837b94 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Mar 2015 11:06:40 +0100 Subject: Update louloulibs to last revision --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index d6a3724..5f3a1bb 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit d6a3724c6a0127a49a9e7adb1090bb7438c8d0f2 +Subproject commit 5f3a1bb54df4de5f332282bbdf791bdce07c71c4 -- cgit v1.2.3 From d88ec5fdf10ecb168355bc38dc81d83ff59a0234 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Mar 2015 11:32:18 +0100 Subject: Update to latest louloulibs revision --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index 5f3a1bb..d0b8695 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 5f3a1bb54df4de5f332282bbdf791bdce07c71c4 +Subproject commit d0b8695ceb13e0c6d72821fe605de36e494afcdf -- cgit v1.2.3 From c243fea660723eba00b65e639b76d0783cb59064 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 4 Mar 2015 05:56:44 +0100 Subject: Update to latest louloulibs revision --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index d0b8695..99757a4 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit d0b8695ceb13e0c6d72821fe605de36e494afcdf +Subproject commit 99757a44b49619ff59cae9e6d983a3b7c20c56bf -- cgit v1.2.3 From ad0465b32051e224f6a234f3ed36494905e59cbf Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 20 Apr 2015 20:33:02 +0200 Subject: Decode incoming JIDs local part according to xep 0106 This let users send message to nicks such as Q@CServe.quakenet.org fix #3047 --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index 99757a4..88d2b13 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 99757a44b49619ff59cae9e6d983a3b7c20c56bf +Subproject commit 88d2b136e5f133f0d0dc01f59449284f663d53ea -- cgit v1.2.3 From 0d706741c6b3a8bdf6b4f8ca0b1ac00cb27bd8b8 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 20 Apr 2015 20:35:32 +0200 Subject: Update louloulibs submodule to the correct revision --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index 88d2b13..b53ae92 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 88d2b136e5f133f0d0dc01f59449284f663d53ea +Subproject commit b53ae922f48f1465a7fa61136f65ec39e38a452e -- cgit v1.2.3 From a8225dc54c019788722bda3bda8d55151c1ccdef Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 21 Apr 2015 15:35:10 +0200 Subject: Properly check for connecting or connected status before reconnecting Note, in our context, is_connecting() includes the resolving part as well as the actual connection (if we are using c-ares) fix #3048 --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index b53ae92..6c812cd 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit b53ae922f48f1465a7fa61136f65ec39e38a452e +Subproject commit 6c812cd86e31569db61cac4e30f77e296d207191 -- cgit v1.2.3 From 71fec776c4d7b99b76a44deae6f333d9cffa1496 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 7 May 2015 17:42:37 +0200 Subject: Update to latest louloulibs fix #3042 --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index 6c812cd..eaa4fbb 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 6c812cd86e31569db61cac4e30f77e296d207191 +Subproject commit eaa4fbba814b56b4fe7ffb62984fddfbb9280291 -- cgit v1.2.3 From fbeb5af364db54c8a82f5ea30b83df441988ea4b Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 13 May 2015 20:17:43 +0200 Subject: Update to latest louloulibs revision, and add test for hostname validity fix #2694 --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index eaa4fbb..89398b5 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit eaa4fbba814b56b4fe7ffb62984fddfbb9280291 +Subproject commit 89398b5d886744c3812b65195308cae57eca2b53 -- cgit v1.2.3 From 897b281e67dc82700db9fd9c2dedc5e01e5871ee Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 27 May 2015 23:44:23 +0200 Subject: Avoid some potential race conditions by blocking the signals we manage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are atomically unblocked in the ppoll/epoll_pwait calls, avoiding any race condition on the check of the “stop” or “reload” booleans. --- louloulibs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs index 89398b5..0f3c118 160000 --- a/louloulibs +++ b/louloulibs @@ -1 +1 @@ -Subproject commit 89398b5d886744c3812b65195308cae57eca2b53 +Subproject commit 0f3c1183e2bf0941ae2bffd3f31577bce4f3001c -- cgit v1.2.3 From e1a7114c8daa10589c830ce972cf461c3540111b Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 28 May 2015 23:42:52 +0200 Subject: louloulibs is directly included, instead of being a submodule Because this is a nightmare to manage --- louloulibs | 1 - louloulibs/CMakeLists.txt | 146 ++++++ louloulibs/cmake/Modules/FindBOTAN.cmake | 35 ++ louloulibs/cmake/Modules/FindCARES.cmake | 37 ++ louloulibs/cmake/Modules/FindICONV.cmake | 60 +++ louloulibs/cmake/Modules/FindLIBIDN.cmake | 41 ++ louloulibs/cmake/Modules/FindLIBUUID.cmake | 41 ++ louloulibs/cmake/Modules/FindSYSTEMD.cmake | 39 ++ louloulibs/config/config.cpp | 122 +++++ louloulibs/config/config.hpp | 103 +++++ louloulibs/logger/logger.cpp | 38 ++ louloulibs/logger/logger.hpp | 80 ++++ louloulibs/louloulibs.h.cmake | 8 + louloulibs/network/dns_handler.cpp | 112 +++++ louloulibs/network/dns_handler.hpp | 62 +++ louloulibs/network/dns_socket_handler.cpp | 45 ++ louloulibs/network/dns_socket_handler.hpp | 46 ++ louloulibs/network/poller.cpp | 225 +++++++++ louloulibs/network/poller.hpp | 95 ++++ louloulibs/network/socket_handler.hpp | 45 ++ louloulibs/network/tcp_socket_handler.cpp | 590 ++++++++++++++++++++++++ louloulibs/network/tcp_socket_handler.hpp | 293 ++++++++++++ louloulibs/utils/encoding.cpp | 254 ++++++++++ louloulibs/utils/encoding.hpp | 38 ++ louloulibs/utils/reload.cpp | 13 + louloulibs/utils/reload.hpp | 10 + louloulibs/utils/revstr.cpp | 9 + louloulibs/utils/revstr.hpp | 11 + louloulibs/utils/scopeguard.hpp | 89 ++++ louloulibs/utils/sha1.cpp | 154 +++++++ louloulibs/utils/sha1.hpp | 35 ++ louloulibs/utils/split.cpp | 19 + louloulibs/utils/split.hpp | 12 + louloulibs/utils/timed_events.cpp | 62 +++ louloulibs/utils/timed_events.hpp | 132 ++++++ louloulibs/utils/timed_events_manager.cpp | 81 ++++ louloulibs/utils/tolower.cpp | 13 + louloulibs/utils/tolower.hpp | 11 + louloulibs/xmpp/adhoc_command.cpp | 105 +++++ louloulibs/xmpp/adhoc_command.hpp | 43 ++ louloulibs/xmpp/adhoc_commands_handler.cpp | 133 ++++++ louloulibs/xmpp/adhoc_commands_handler.hpp | 74 +++ louloulibs/xmpp/adhoc_session.cpp | 37 ++ louloulibs/xmpp/adhoc_session.hpp | 70 +++ louloulibs/xmpp/body.hpp | 12 + louloulibs/xmpp/jid.cpp | 101 ++++ louloulibs/xmpp/jid.hpp | 36 ++ louloulibs/xmpp/roster.cpp | 21 + louloulibs/xmpp/roster.hpp | 71 +++ louloulibs/xmpp/xmpp_component.cpp | 716 +++++++++++++++++++++++++++++ louloulibs/xmpp/xmpp_component.hpp | 240 ++++++++++ louloulibs/xmpp/xmpp_parser.cpp | 169 +++++++ louloulibs/xmpp/xmpp_parser.hpp | 126 +++++ louloulibs/xmpp/xmpp_stanza.cpp | 274 +++++++++++ louloulibs/xmpp/xmpp_stanza.hpp | 160 +++++++ 55 files changed, 5594 insertions(+), 1 deletion(-) delete mode 160000 louloulibs create mode 100644 louloulibs/CMakeLists.txt create mode 100644 louloulibs/cmake/Modules/FindBOTAN.cmake create mode 100644 louloulibs/cmake/Modules/FindCARES.cmake create mode 100644 louloulibs/cmake/Modules/FindICONV.cmake create mode 100644 louloulibs/cmake/Modules/FindLIBIDN.cmake create mode 100644 louloulibs/cmake/Modules/FindLIBUUID.cmake create mode 100644 louloulibs/cmake/Modules/FindSYSTEMD.cmake create mode 100644 louloulibs/config/config.cpp create mode 100644 louloulibs/config/config.hpp create mode 100644 louloulibs/logger/logger.cpp create mode 100644 louloulibs/logger/logger.hpp create mode 100644 louloulibs/louloulibs.h.cmake create mode 100644 louloulibs/network/dns_handler.cpp create mode 100644 louloulibs/network/dns_handler.hpp create mode 100644 louloulibs/network/dns_socket_handler.cpp create mode 100644 louloulibs/network/dns_socket_handler.hpp create mode 100644 louloulibs/network/poller.cpp create mode 100644 louloulibs/network/poller.hpp create mode 100644 louloulibs/network/socket_handler.hpp create mode 100644 louloulibs/network/tcp_socket_handler.cpp create mode 100644 louloulibs/network/tcp_socket_handler.hpp create mode 100644 louloulibs/utils/encoding.cpp create mode 100644 louloulibs/utils/encoding.hpp create mode 100644 louloulibs/utils/reload.cpp create mode 100644 louloulibs/utils/reload.hpp create mode 100644 louloulibs/utils/revstr.cpp create mode 100644 louloulibs/utils/revstr.hpp create mode 100644 louloulibs/utils/scopeguard.hpp create mode 100644 louloulibs/utils/sha1.cpp create mode 100644 louloulibs/utils/sha1.hpp create mode 100644 louloulibs/utils/split.cpp create mode 100644 louloulibs/utils/split.hpp create mode 100644 louloulibs/utils/timed_events.cpp create mode 100644 louloulibs/utils/timed_events.hpp create mode 100644 louloulibs/utils/timed_events_manager.cpp create mode 100644 louloulibs/utils/tolower.cpp create mode 100644 louloulibs/utils/tolower.hpp create mode 100644 louloulibs/xmpp/adhoc_command.cpp create mode 100644 louloulibs/xmpp/adhoc_command.hpp create mode 100644 louloulibs/xmpp/adhoc_commands_handler.cpp create mode 100644 louloulibs/xmpp/adhoc_commands_handler.hpp create mode 100644 louloulibs/xmpp/adhoc_session.cpp create mode 100644 louloulibs/xmpp/adhoc_session.hpp create mode 100644 louloulibs/xmpp/body.hpp create mode 100644 louloulibs/xmpp/jid.cpp create mode 100644 louloulibs/xmpp/jid.hpp create mode 100644 louloulibs/xmpp/roster.cpp create mode 100644 louloulibs/xmpp/roster.hpp create mode 100644 louloulibs/xmpp/xmpp_component.cpp create mode 100644 louloulibs/xmpp/xmpp_component.hpp create mode 100644 louloulibs/xmpp/xmpp_parser.cpp create mode 100644 louloulibs/xmpp/xmpp_parser.hpp create mode 100644 louloulibs/xmpp/xmpp_stanza.cpp create mode 100644 louloulibs/xmpp/xmpp_stanza.hpp (limited to 'louloulibs') diff --git a/louloulibs b/louloulibs deleted file mode 160000 index 0f3c118..0000000 --- a/louloulibs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0f3c1183e2bf0941ae2bffd3f31577bce4f3001c diff --git a/louloulibs/CMakeLists.txt b/louloulibs/CMakeLists.txt new file mode 100644 index 0000000..bf53504 --- /dev/null +++ b/louloulibs/CMakeLists.txt @@ -0,0 +1,146 @@ +cmake_minimum_required(VERSION 2.6) + +set(${PROJECT_NAME}_VERSION_MAJOR 1) +set(${PROJECT_NAME}_VERSION_MINOR 0) +set(${PROJECT_NAME}_VERSION_SUFFIX "~dev") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -pedantic -Wall -Wextra") + +# Define a __FILENAME__ macro to get the filename of each file, instead of +# the full path as in __FILE__ +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__FILENAME__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'") + +# +## Look for external libraries +# +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") +include(FindEXPAT) +find_package(EXPAT REQUIRED) +find_package(ICONV REQUIRED) +find_package(LIBUUID REQUIRED) + +if(WITH_LIBIDN) + find_package(LIBIDN REQUIRED) +elseif(NOT WITHOUT_LIBIDN) + find_package(LIBIDN) +endif() + +if(WITH_SYSTEMD) + find_package(SYSTEMD REQUIRED) +elseif(NOT WITHOUT_SYSTEMD) + find_package(SYSTEMD) +endif() + +if(WITH_BOTAN) + find_package(BOTAN REQUIRED) +elseif(NOT WITHOUT_BOTAN) + find_package(BOTAN) +endif() + +if(WITH_CARES) + find_package(CARES REQUIRED) +elseif(NOT WITHOUT_CARES) + find_package(CARES) +endif() + +# To be able to include the config.h file generated by cmake +include_directories("${CMAKE_CURRENT_BINARY_DIR}") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}") +include_directories(${EXPAT_INCLUDE_DIRS}) +include_directories(${ICONV_INCLUDE_DIRS}) +include_directories(${LIBUUID_INCLUDE_DIRS}) + +set(EXPAT_INCLUDE_DIRS ${EXPAT_INCLUDE_DIRS} PARENT_SCOPE) +set(ICONV_INCLUDE_DIRS ${ICONV_INCLUDE_DIRS} PARENT_SCOPE) +set(LIBUUID_INCLUDE_DIRS ${LIBUUID_INCLUDE_DIRS} PARENT_SCOPE) + +if(LIBIDN_FOUND) + include_directories(${LIBIDN_INCLUDE_DIRS}) + set(LIBDIN_FOUND ${LIBDIN_FOUND} PARENT_SCOPE) + set(LIBDIN_INCLUDE_DIRS ${LIBDIN_INCLUDE_DIRS} PARENT_SCOPE) +endif() + +if(SYSTEMD_FOUND) + include_directories(${SYSTEMD_INCLUDE_DIRS}) + set(SYSTEMD_FOUND ${SYSTEMD_FOUND} PARENT_SCOPE) + set(SYSTEMD_INCLUDE_DIRS ${SYSTEMD_INCLUDE_DIRS} PARENT_SCOPE) +endif() + +if(BOTAN_FOUND) + include_directories(SYSTEM ${BOTAN_INCLUDE_DIRS}) + set(BOTAN_FOUND ${BOTAN_FOUND} PARENT_SCOPE) + set(BOTAN_INCLUDE_DIRS ${BOTAN_INCLUDE_DIRS} PARENT_SCOPE) +endif() + +if(CARES_FOUND) + include_directories(${CARES_INCLUDE_DIRS}) + set(CARES_FOUND ${CARES_FOUND} PARENT_SCOPE) + set(CARES_INCLUDE_DIRS ${CARES_INCLUDE_DIRS} PARENT_SCOPE) +endif() + +set(POLLER_DOCSTRING "Choose the poller between POLL and EPOLL (Linux-only)") +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(POLLER "EPOLL" CACHE STRING ${POLLER_DOCSTRING}) +else() + set(POLLER "POLL" CACHE STRING ${POLLER_DOCSTRING}) +endif() +if((NOT ${POLLER} MATCHES "POLL") AND + (NOT ${POLLER} MATCHES "EPOLL")) + message(FATAL_ERROR "POLLER must be either POLL or EPOLL") +endif() + +# +## utils +# +file(GLOB source_utils + utils/*.[hc]pp) +add_library(utils STATIC ${source_utils}) +target_link_libraries(utils ${ICONV_LIBRARIES}) + +# +## config +# +file(GLOB source_config + config/*.[hc]pp) +add_library(config STATIC ${source_config}) +target_link_libraries(config utils) + +# +## logger +# +file(GLOB source_logger + logger/*.[hc]pp) +add_library(logger STATIC ${source_logger}) +target_link_libraries(logger config) + +# +## network +# +file(GLOB source_network + network/*.[hc]pp) +add_library(network STATIC ${source_network}) +target_link_libraries(network logger) +if(BOTAN_FOUND) + target_link_libraries(network ${BOTAN_LIBRARIES}) +endif() +if(CARES_FOUND) + target_link_libraries(network ${CARES_LIBRARIES}) +endif() + +# +## xmpplib +# +file(GLOB source_xmpplib + xmpp/*.[hc]pp) +add_library(xmpplib STATIC ${source_xmpplib}) +target_link_libraries(xmpplib network utils logger + ${EXPAT_LIBRARIES} + ${LIBUUID_LIBRARIES}) +if(LIBIDN_FOUND) + target_link_libraries(xmpplib ${LIBIDN_LIBRARIES}) +endif() +if(SYSTEMD_FOUND) + target_link_libraries(xmpplib ${SYSTEMD_LIBRARIES}) +endif() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/louloulibs.h.cmake ${CMAKE_BINARY_DIR}/src/louloulibs.h) diff --git a/louloulibs/cmake/Modules/FindBOTAN.cmake b/louloulibs/cmake/Modules/FindBOTAN.cmake new file mode 100644 index 0000000..a12bd35 --- /dev/null +++ b/louloulibs/cmake/Modules/FindBOTAN.cmake @@ -0,0 +1,35 @@ +# - Find botan +# Find the botan cryptographic library +# +# This module defines the following variables: +# BOTAN_FOUND - True if library and include directory are found +# If set to TRUE, the following are also defined: +# BOTAN_INCLUDE_DIRS - The directory where to find the header file +# BOTAN_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. +# BOTAN_LIBRARY +# BOTAN_INCLUDE_DIR +# +# This file is in the public domain + +find_path(BOTAN_INCLUDE_DIRS NAMES botan/botan.h + PATH_SUFFIXES botan-1.11 + DOC "The botan include directory") + +find_library(BOTAN_LIBRARIES NAMES botan botan-1.11 + DOC "The botan library") + +# Use some standard module to handle the QUIETLY and REQUIRED arguments, and +# set BOTAN_FOUND to TRUE if these two variables are set. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(BOTAN REQUIRED_VARS BOTAN_LIBRARIES BOTAN_INCLUDE_DIRS) + +if(BOTAN_FOUND) + set(BOTAN_LIBRARY ${BOTAN_LIBRARIES}) + set(BOTAN_INCLUDE_DIR ${BOTAN_INCLUDE_DIRS}) +endif() + +mark_as_advanced(BOTAN_INCLUDE_DIRS BOTAN_LIBRARIES) diff --git a/louloulibs/cmake/Modules/FindCARES.cmake b/louloulibs/cmake/Modules/FindCARES.cmake new file mode 100644 index 0000000..c4c757a --- /dev/null +++ b/louloulibs/cmake/Modules/FindCARES.cmake @@ -0,0 +1,37 @@ +# - Find c-ares +# Find the c-ares library, and more particularly the stringprep header. +# +# This module defines the following variables: +# CARES_FOUND - True if library and include directory are found +# If set to TRUE, the following are also defined: +# CARES_INCLUDE_DIRS - The directory where to find the header file +# CARES_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. +# CARES_INCLUDE_DIR +# CARES_LIBRARY +# +# This file is in the public domain + +if(NOT CARES_FOUND) + find_path(CARES_INCLUDE_DIRS NAMES ares.h + DOC "The c-ares include directory") + + find_library(CARES_LIBRARIES NAMES cares + DOC "The c-ares library") + + # Use some standard module to handle the QUIETLY and REQUIRED arguments, and + # set CARES_FOUND to TRUE if these two variables are set. + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(CARES REQUIRED_VARS CARES_LIBRARIES CARES_INCLUDE_DIRS) + + # Compatibility for all the ways of writing these variables + if(CARES_FOUND) + set(CARES_INCLUDE_DIR ${CARES_INCLUDE_DIRS}) + set(CARES_LIBRARY ${CARES_LIBRARIES}) + endif() +endif() + +mark_as_advanced(CARES_INCLUDE_DIRS CARES_LIBRARIES) diff --git a/louloulibs/cmake/Modules/FindICONV.cmake b/louloulibs/cmake/Modules/FindICONV.cmake new file mode 100644 index 0000000..7ca173f --- /dev/null +++ b/louloulibs/cmake/Modules/FindICONV.cmake @@ -0,0 +1,60 @@ +# - Find iconv +# Find the iconv (character set conversion) library +# +# This module defines the following variables: +# ICONV_FOUND - True if library and include directory are found +# If set to TRUE, the following are also defined: +# ICONV_INCLUDE_DIRS - The directory where to find the header file +# ICONV_LIBRARIES - Where to find the library file +# ICONV_SECOND_ARGUMENT_IS_CONST - The second argument for iconv() is const +# +# 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. +# ICONV_LIBRARY +# ICONV_INCLUDE_DIR +# +# This file is in the public domain + +find_path(ICONV_INCLUDE_DIRS NAMES iconv.h + DOC "The iconv include directory") + +find_library(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c + DOC "The iconv library") + +# Use some standard module to handle the QUIETLY and REQUIRED arguments, and +# set ICONV_FOUND to TRUE if these two variables are set. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Iconv REQUIRED_VARS ICONV_LIBRARIES ICONV_INCLUDE_DIRS) + +# Check if the prototype is +# size_t iconv(iconv_t cd, char** inbuf, size_t* inbytesleft, +# char** outbuf, size_t* outbytesleft); +# or +# size_t iconv (iconv_t cd, const char** inbuf, size_t* inbytesleft, +# char** outbuf, size_t* outbytesleft); +if(ICONV_FOUND) + include(CheckCXXSourceCompiles) + + # Set the parameters needed to compile the following code. + set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARIES}) + + check_cxx_source_compiles(" + #include + int main(){ + iconv_t conv = 0; + const char* in = 0; + size_t ilen = 0; + char* out = 0; + size_t olen = 0; + iconv(conv, &in, &ilen, &out, &olen); + return 0;}" + ICONV_SECOND_ARGUMENT_IS_CONST) + +# Compatibility for all the ways of writing these variables + set(ICONV_LIBRARY ${ICONV_LIBRARIES}) + set(ICONV_INCLUDE_DIR ${ICONV_INCLUDE_DIRS}) +endif() + +mark_as_advanced(ICONV_INCLUDE_DIRS ICONV_LIBRARIES ICONV_SECOND_ARGUMENT_IS_CONST) \ No newline at end of file diff --git a/louloulibs/cmake/Modules/FindLIBIDN.cmake b/louloulibs/cmake/Modules/FindLIBIDN.cmake new file mode 100644 index 0000000..611a6a8 --- /dev/null +++ b/louloulibs/cmake/Modules/FindLIBIDN.cmake @@ -0,0 +1,41 @@ +# - Find libidn +# Find the libidn library, and more particularly the stringprep header. +# +# This module defines the following variables: +# LIBIDN_FOUND - True if library and include directory are found +# If set to TRUE, the following are also defined: +# LIBIDN_INCLUDE_DIRS - The directory where to find the header file +# LIBIDN_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. +# LIBIDN_INCLUDE_DIR +# LIBIDN_LIBRARY +# +# This file is in the public domain + +include(FindPkgConfig) +pkg_check_modules(LIBIDN libidn) + +if(NOT LIBIDN_FOUND) + find_path(LIBIDN_INCLUDE_DIRS NAMES stringprep.h + DOC "The libidn include directory") + + # The library containing the stringprep module is libidn + find_library(LIBIDN_LIBRARIES NAMES idn + DOC "The libidn library") + + # Use some standard module to handle the QUIETLY and REQUIRED arguments, and + # set LIBIDN_FOUND to TRUE if these two variables are set. + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LIBIDN REQUIRED_VARS LIBIDN_LIBRARIES LIBIDN_INCLUDE_DIRS) + + # Compatibility for all the ways of writing these variables + if(LIBIDN_FOUND) + set(LIBIDN_INCLUDE_DIR ${LIBIDN_INCLUDE_DIRS}) + set(LIBIDN_LIBRARY ${LIBIDN_LIBRARIES}) + endif() +endif() + +mark_as_advanced(LIBIDN_INCLUDE_DIRS LIBIDN_LIBRARIES) \ No newline at end of file diff --git a/louloulibs/cmake/Modules/FindLIBUUID.cmake b/louloulibs/cmake/Modules/FindLIBUUID.cmake new file mode 100644 index 0000000..17d3c42 --- /dev/null +++ b/louloulibs/cmake/Modules/FindLIBUUID.cmake @@ -0,0 +1,41 @@ +# - Find libuuid +# Find the libuuid library +# +# This module defines the following variables: +# LIBUUID_FOUND - True if library and include directory are found +# If set to TRUE, the following are also defined: +# LIBUUID_INCLUDE_DIRS - The directory where to find the header file +# LIBUUID_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. +# LIBUUID_INCLUDE_DIR +# LIBUUID_LIBRARY +# +# This file is in the public domain + +include(FindPkgConfig) +pkg_check_modules(LIBUUID uuid) + +if(NOT LIBUUID_FOUND) + find_path(LIBUUID_INCLUDE_DIRS NAMES uuid.h + PATH_SUFFIXES uuid + DOC "The libuuid include directory") + + find_library(LIBUUID_LIBRARIES NAMES uuid + DOC "The libuuid library") + + # Use some standard module to handle the QUIETLY and REQUIRED arguments, and + # set LIBUUID_FOUND to TRUE if these two variables are set. + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LIBUUID REQUIRED_VARS LIBUUID_LIBRARIES LIBUUID_INCLUDE_DIRS) + + # Compatibility for all the ways of writing these variables + if(LIBUUID_FOUND) + set(LIBUUID_INCLUDE_DIR ${LIBUUID_INCLUDE_DIRS}) + set(LIBUUID_LIBRARY ${LIBUUID_LIBRARIES}) + endif() +endif() + +mark_as_advanced(LIBUUID_INCLUDE_DIRS LIBUUID_LIBRARIES) diff --git a/louloulibs/cmake/Modules/FindSYSTEMD.cmake b/louloulibs/cmake/Modules/FindSYSTEMD.cmake new file mode 100644 index 0000000..c7decde --- /dev/null +++ b/louloulibs/cmake/Modules/FindSYSTEMD.cmake @@ -0,0 +1,39 @@ +# - Find SystemdDaemon +# Find the systemd daemon library +# +# This module defines the following variables: +# SYSTEMD_FOUND - True if library and include directory are found +# If set to TRUE, the following are also defined: +# SYSTEMD_INCLUDE_DIRS - The directory where to find the header file +# SYSTEMD_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. +# SYSTEMD_LIBRARY +# SYSTEMD_INCLUDE_DIR +# +# This file is in the public domain + +include(FindPkgConfig) +pkg_check_modules(SYSTEMD libsystemd) + +if(NOT SYSTEMD_FOUND) + find_path(SYSTEMD_INCLUDE_DIRS NAMES systemd/sd-daemon.h + DOC "The Systemd include directory") + + find_library(SYSTEMD_LIBRARIES NAMES systemd + DOC "The Systemd library") + + # Use some standard module to handle the QUIETLY and REQUIRED arguments, and + # set SYSTEMD_FOUND to TRUE if these two variables are set. + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(SYSTEMD REQUIRED_VARS SYSTEMD_LIBRARIES SYSTEMD_INCLUDE_DIRS) + + if(SYSTEMD_FOUND) + set(SYSTEMD_LIBRARY ${SYSTEMD_LIBRARIES}) + set(SYSTEMD_INCLUDE_DIR ${SYSTEMD_INCLUDE_DIRS}) + endif() +endif() + +mark_as_advanced(SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES) \ No newline at end of file diff --git a/louloulibs/config/config.cpp b/louloulibs/config/config.cpp new file mode 100644 index 0000000..3e016f4 --- /dev/null +++ b/louloulibs/config/config.cpp @@ -0,0 +1,122 @@ +#include + +#include +#include + +#include + +std::string Config::filename{}; +bool Config::file_must_exist = false; + +std::string Config::get(const std::string& option, const std::string& def) +{ + Config* self = Config::instance().get(); + auto it = self->values.find(option); + + if (it == self->values.end()) + return def; + return it->second; +} + +int Config::get_int(const std::string& option, const int& def) +{ + Config* self = Config::instance().get(); + std::string res = self->get(option, ""); + if (!res.empty()) + return atoi(res.c_str()); + else + return def; +} + +void Config::set(const std::string& option, const std::string& value, bool save) +{ + Config* self = Config::instance().get(); + self->values[option] = value; + if (save) + { + self->save_to_file(); + self->trigger_configuration_change(); + } +} + +void Config::connect(t_config_changed_callback callback) +{ + Config* self = Config::instance().get(); + self->callbacks.push_back(callback); +} + +void Config::close() +{ + Config* self = Config::instance().get(); + self->values.clear(); + Config::instance().reset(); +} + +/** + * Private methods + */ + +void Config::trigger_configuration_change() +{ + std::vector::iterator it; + for (it = this->callbacks.begin(); it < this->callbacks.end(); ++it) + (*it)(); +} + +std::unique_ptr& Config::instance() +{ + static std::unique_ptr instance; + + if (!instance) + { + instance = std::make_unique(); + instance->read_conf(); + } + return instance; +} + +bool Config::read_conf() +{ + std::ifstream file; + file.open(filename.data()); + if (!file.is_open()) + { + if (Config::file_must_exist) + { + perror(("Error while opening file " + filename + " for reading.").c_str()); + file.exceptions(std::ifstream::failbit); + } + return false; + } + + std::string line; + size_t pos; + std::string option; + std::string value; + while (file.good()) + { + std::getline(file, line); + if (line == "" || line[0] == '#') + continue ; + pos = line.find('='); + if (pos == std::string::npos) + continue ; + option = line.substr(0, pos); + value = line.substr(pos+1); + this->values[option] = value; + } + return true; +} + +void Config::save_to_file() const +{ + std::ofstream file(this->filename.data()); + if (file.fail()) + { + std::cerr << "Could not save config file." << std::endl; + return ; + } + for (auto& it: this->values) + file << it.first << "=" << it.second << std::endl; + file.close(); +} diff --git a/louloulibs/config/config.hpp b/louloulibs/config/config.hpp new file mode 100644 index 0000000..e070816 --- /dev/null +++ b/louloulibs/config/config.hpp @@ -0,0 +1,103 @@ +/** + * Read the config file and save all the values in a map. + * Also, a singleton. + * + * Use Config::filename = "bla" to set the filename you want to use. + * + * If you want to exit if the file does not exist when it is open for + * reading, set Config::file_must_exist = true. + * + * Config::get() can the be used to access the values in the conf. + * + * Use Config::close() when you're done getting/setting value. This will + * save the config into the file. + */ + +#ifndef CONFIG_INCLUDED +# define CONFIG_INCLUDED + +#include +#include +#include +#include +#include +#include + +typedef std::function t_config_changed_callback; + +class Config +{ +public: + Config(){}; + ~Config(){}; + /** + * returns a value from the config. If it doesn’t exist, use + * the second argument as the default. + * @param option The option we want + * @param def The default value in case the option does not exist + */ + static std::string get(const std::string&, const std::string&); + /** + * returns a value from the config. If it doesn’t exist, use + * the second argument as the default. + * @param option The option we want + * @param def The default value in case the option does not exist + */ + static int get_int(const std::string&, const int&); + /** + * Set a value for the given option. And write all the config + * in the file from which it was read if boolean is set. + * @param option The option to set + * @param value The value to use + * @param save if true, save the config file + */ + static void set(const std::string&, const std::string&, bool save = false); + /** + * Adds a function to a list. This function will be called whenever a + * configuration change occurs. + */ + static void connect(t_config_changed_callback); + /** + * Close the config file, saving it to the file is save == true. + */ + static void close(); + + /** + * Set the value of the filename to use, before calling any method. + */ + static std::string filename; + /** + * Set to true if you want an exception to be raised if the file does not + * exist when reading it. + */ + static bool file_must_exist; + +private: + /** + * Get the singleton instance + */ + static std::unique_ptr& instance(); + /** + * Read the configuration file at the given path. + */ + bool read_conf(); + /** + * Write all the config values into the configuration file + */ + void save_to_file() const; + /** + * Call all the callbacks previously registered using connect(). + * This is used to notify any class that a configuration change occured. + */ + void trigger_configuration_change(); + + std::map values; + std::vector callbacks; + + Config(const Config&) = delete; + Config& operator=(const Config&) = delete; + Config(Config&&) = delete; + Config& operator=(Config&&) = delete; +}; + +#endif // CONFIG_INCLUDED diff --git a/louloulibs/logger/logger.cpp b/louloulibs/logger/logger.cpp new file mode 100644 index 0000000..7336579 --- /dev/null +++ b/louloulibs/logger/logger.cpp @@ -0,0 +1,38 @@ +#include +#include + +Logger::Logger(const int log_level): + log_level(log_level), + stream(std::cout.rdbuf()) +{ +} + +Logger::Logger(const int log_level, const std::string& log_file): + log_level(log_level), + ofstream(log_file.data(), std::ios_base::app), + stream(ofstream.rdbuf()) +{ +} + +std::unique_ptr& Logger::instance() +{ + static std::unique_ptr instance; + + if (!instance) + { + const std::string log_file = Config::get("log_file", ""); + const int log_level = Config::get_int("log_level", 0); + if (log_file.empty()) + instance = std::make_unique(log_level); + else + instance = std::make_unique(log_level, log_file); + } + return instance; +} + +std::ostream& Logger::get_stream(const int lvl) +{ + if (lvl >= this->log_level) + return this->stream; + return this->null_stream; +} diff --git a/louloulibs/logger/logger.hpp b/louloulibs/logger/logger.hpp new file mode 100644 index 0000000..78b1278 --- /dev/null +++ b/louloulibs/logger/logger.hpp @@ -0,0 +1,80 @@ +#ifndef LOGGER_INCLUDED +# define LOGGER_INCLUDED + +/** + * Singleton used in logger macros to write into files or stdout, with + * various levels of severity. + * Only the macros should be used. + * @class Logger + */ + +#include +#include +#include + +#define debug_lvl 0 +#define info_lvl 1 +#define warning_lvl 2 +#define error_lvl 3 + +#include "louloulibs.h" +#ifdef SYSTEMD_FOUND +# include +#else +# define SD_DEBUG "[DEBUG]: " +# define SD_INFO "[INFO]: " +# define SD_WARNING "[WARNING]: " +# define SD_ERR "[ERROR]: " +#endif + +// Macro defined to get the filename instead of the full path. But if it is +// not properly defined by the build system, we fallback to __FILE__ +#ifndef __FILENAME__ +# define __FILENAME__ __FILE__ +#endif + +#define WHERE\ + __FILENAME__ << ":" << __LINE__ + +#define log_debug(text)\ + Logger::instance()->get_stream(debug_lvl) << SD_DEBUG << WHERE << ":\t" << text << std::endl; + +#define log_info(text)\ + Logger::instance()->get_stream(info_lvl) << SD_INFO << WHERE << ":\t" << text << std::endl; + +#define log_warning(text)\ + Logger::instance()->get_stream(warning_lvl) << SD_WARNING << WHERE << ":\t" << text << std::endl; + +#define log_error(text)\ + Logger::instance()->get_stream(error_lvl) << SD_ERR << WHERE << ":\t" << text << std::endl; + +/** + * Juste a structure representing a stream doing nothing with its input. + */ +class nullstream: public std::ostream +{ +public: + nullstream(): + std::ostream(0) + { } +}; + +class Logger +{ +public: + static std::unique_ptr& instance(); + std::ostream& get_stream(const int); + Logger(const int log_level, const std::string& log_file); + Logger(const int log_level); + +private: + Logger(const Logger&); + Logger& operator=(const Logger&); + + const int log_level; + std::ofstream ofstream; + nullstream null_stream; + std::ostream stream; +}; + +#endif // LOGGER_INCLUDED diff --git a/louloulibs/louloulibs.h.cmake b/louloulibs/louloulibs.h.cmake new file mode 100644 index 0000000..707d3fe --- /dev/null +++ b/louloulibs/louloulibs.h.cmake @@ -0,0 +1,8 @@ +#define SYSTEM_NAME "${CMAKE_SYSTEM}" +#cmakedefine ICONV_SECOND_ARGUMENT_IS_CONST +#cmakedefine LIBIDN_FOUND +#cmakedefine SYSTEMD_FOUND +#cmakedefine POLLER ${POLLER} +#cmakedefine BOTAN_FOUND +#cmakedefine CARES_FOUND +#cmakedefine SOFTWARE_VERSION "${SOFTWARE_VERSION}" \ No newline at end of file diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp new file mode 100644 index 0000000..ec53683 --- /dev/null +++ b/louloulibs/network/dns_handler.cpp @@ -0,0 +1,112 @@ +#include +#ifdef CARES_FOUND + +#include +#include +#include +#include + +#include +#include + +DNSHandler DNSHandler::instance; + +using namespace std::string_literals; + +void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent) +{ + TCPSocketHandler* socket_handler = static_cast(arg); + socket_handler->on_hostname4_resolved(status, hostent); +} + +void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent) +{ + TCPSocketHandler* socket_handler = static_cast(arg); + socket_handler->on_hostname6_resolved(status, hostent); +} + +DNSHandler::DNSHandler() +{ + int ares_error; + if ((ares_error = ::ares_library_init(ARES_LIB_INIT_ALL)) != 0) + throw std::runtime_error("Failed to initialize c-ares lib: "s + ares_strerror(ares_error)); + if ((ares_error = ::ares_init(&this->channel)) != ARES_SUCCESS) + throw std::runtime_error("Failed to initialize c-ares channel: "s + ares_strerror(ares_error)); +} + +ares_channel& DNSHandler::get_channel() +{ + return this->channel; +} + +void DNSHandler::destroy() +{ + this->socket_handlers.clear(); + ::ares_destroy(this->channel); + ::ares_library_cleanup(); +} + +void DNSHandler::gethostbyname(const std::string& name, + TCPSocketHandler* socket_handler, int family) +{ + socket_handler->free_cares_addrinfo(); + if (family == AF_INET) + ::ares_gethostbyname(this->channel, name.data(), family, + &::on_hostname4_resolved, socket_handler); + else + ::ares_gethostbyname(this->channel, name.data(), family, + &::on_hostname6_resolved, socket_handler); +} + +void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) +{ + fd_set readers; + fd_set writers; + + FD_ZERO(&readers); + FD_ZERO(&writers); + + int ndfs = ::ares_fds(this->channel, &readers, &writers); + // For each existing DNS socket, see if we are still supposed to watch it, + // if not then erase it + this->socket_handlers.erase( + std::remove_if(this->socket_handlers.begin(), this->socket_handlers.end(), + [&readers](const auto& dns_socket) + { + return !FD_ISSET(dns_socket->get_socket(), &readers); + }), + this->socket_handlers.end()); + + for (auto i = 0; i < ndfs; ++i) + { + bool read = FD_ISSET(i, &readers); + bool write = FD_ISSET(i, &writers); + // Look for the DNSSocketHandler with this fd + auto it = std::find_if(this->socket_handlers.begin(), + this->socket_handlers.end(), + [i](const auto& socket_handler) + { + return i == socket_handler->get_socket(); + }); + if (!read && !write) // No need to read or write to it + { // If found, erase it and stop watching it because it is not + // needed anymore + if (it != this->socket_handlers.end()) + // The socket destructor removes it from the poller + this->socket_handlers.erase(it); + } + else // We need to write and/or read to it + { // If not found, create it because we need to watch it + if (it == this->socket_handlers.end()) + { + this->socket_handlers.emplace_front(std::make_unique(poller, i)); + it = this->socket_handlers.begin(); + } + poller->add_socket_handler(it->get()); + if (write) + poller->watch_send_events(it->get()); + } + } +} + +#endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp new file mode 100644 index 0000000..a515f52 --- /dev/null +++ b/louloulibs/network/dns_handler.hpp @@ -0,0 +1,62 @@ +#ifndef DNS_HANDLER_HPP_INCLUDED +#define DNS_HANDLER_HPP_INCLUDED + +#include +#ifdef CARES_FOUND + +class TCPSocketHandler; +class Poller; +class DNSSocketHandler; + +# include +# include +# include +# include + +void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent); +void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent); + +/** + * Class managing DNS resolution. It should only be statically instanciated + * once in SocketHandler. It manages ares channel and calls various + * functions of that library. + */ + +class DNSHandler +{ +public: + DNSHandler(); + ~DNSHandler() = default; + void gethostbyname(const std::string& name, TCPSocketHandler* socket_handler, + int family); + /** + * Call ares_fds to know what fd needs to be watched by the poller, create + * or destroy DNSSocketHandlers depending on the result. + */ + void watch_dns_sockets(std::shared_ptr& poller); + /** + * Destroy and stop watching all the DNS sockets. Then de-init the channel + * and library. + */ + void destroy(); + ares_channel& get_channel(); + + static DNSHandler instance; + +private: + /** + * The list of sockets that needs to be watched, according to the last + * call to ares_fds. DNSSocketHandlers are added to it or removed from it + * in the watch_dns_sockets() method + */ + std::list> socket_handlers; + ares_channel channel; + + DNSHandler(const DNSHandler&) = delete; + DNSHandler(DNSHandler&&) = delete; + DNSHandler& operator=(const DNSHandler&) = delete; + DNSHandler& operator=(DNSHandler&&) = delete; +}; + +#endif /* CARES_FOUND */ +#endif /* DNS_HANDLER_HPP_INCLUDED */ diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp new file mode 100644 index 0000000..124c9b2 --- /dev/null +++ b/louloulibs/network/dns_socket_handler.cpp @@ -0,0 +1,45 @@ +#include +#ifdef CARES_FOUND + +#include +#include +#include + +#include + +DNSSocketHandler::DNSSocketHandler(std::shared_ptr poller, + const socket_t socket): + SocketHandler(poller, socket) +{ +} + +DNSSocketHandler::~DNSSocketHandler() +{ +} + +void DNSSocketHandler::connect() +{ +} + +void DNSSocketHandler::on_recv() +{ + // always stop watching send and read events. We will re-watch them if the + // next call to ares_fds tell us to + this->poller->remove_socket_handler(this->socket); + ::ares_process_fd(DNSHandler::instance.get_channel(), this->socket, ARES_SOCKET_BAD); +} + +void DNSSocketHandler::on_send() +{ + // always stop watching send and read events. We will re-watch them if the + // next call to ares_fds tell us to + this->poller->remove_socket_handler(this->socket); + ::ares_process_fd(DNSHandler::instance.get_channel(), ARES_SOCKET_BAD, this->socket); +} + +bool DNSSocketHandler::is_connected() const +{ + return true; +} + +#endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp new file mode 100644 index 0000000..ad119e1 --- /dev/null +++ b/louloulibs/network/dns_socket_handler.hpp @@ -0,0 +1,46 @@ +#ifndef DNS_SOCKET_HANDLER_HPP +# define DNS_SOCKET_HANDLER_HPP + +#include +#ifdef CARES_FOUND + +#include +#include + +/** + * Manage a socket returned by ares_fds. We do not create, open or close the + * socket ourself: this is done by c-ares. We just call ares_process_fd() + * with the correct parameters, depending on what can be done on that socket + * (Poller reported it to be writable or readeable) + */ + +class DNSSocketHandler: public SocketHandler +{ +public: + explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); + ~DNSSocketHandler(); + /** + * Just call dns_process_fd, c-ares will do its work of send()ing or + * recv()ing the data it wants on that socket. + */ + void on_recv() override final; + void on_send() override final; + /** + * Do nothing, because we are always considered to be connected, since the + * connection is done by c-ares and not by us. + */ + void connect() override final; + /** + * Always true, see the comment for connect() + */ + bool is_connected() const override final; + +private: + DNSSocketHandler(const DNSSocketHandler&) = delete; + DNSSocketHandler(DNSSocketHandler&&) = delete; + DNSSocketHandler& operator=(const DNSSocketHandler&) = delete; + DNSSocketHandler& operator=(DNSSocketHandler&&) = delete; +}; + +#endif // CARES_FOUND +#endif // DNS_SOCKET_HANDLER_HPP diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp new file mode 100644 index 0000000..329e1c8 --- /dev/null +++ b/louloulibs/network/poller.cpp @@ -0,0 +1,225 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +Poller::Poller() +{ +#if POLLER == POLL + this->nfds = 0; +#elif POLLER == EPOLL + this->epfd = ::epoll_create1(0); + if (this->epfd == -1) + { + log_error("epoll failed: " << strerror(errno)); + throw std::runtime_error("Could not create epoll instance"); + } +#endif +} + +Poller::~Poller() +{ +} + +void Poller::add_socket_handler(SocketHandler* socket_handler) +{ + // Don't do anything if the socket is already managed + const auto it = this->socket_handlers.find(socket_handler->get_socket()); + if (it != this->socket_handlers.end()) + return ; + + this->socket_handlers.emplace(socket_handler->get_socket(), socket_handler); + + // We always watch all sockets for receive events +#if POLLER == POLL + this->fds[this->nfds].fd = socket_handler->get_socket(); + this->fds[this->nfds].events = POLLIN; + this->nfds++; +#endif +#if POLLER == EPOLL + struct epoll_event event = {EPOLLIN, {socket_handler}}; + const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_ADD, socket_handler->get_socket(), &event); + if (res == -1) + { + log_error("epoll_ctl failed: " << strerror(errno)); + throw std::runtime_error("Could not add socket to epoll"); + } +#endif +} + +void Poller::remove_socket_handler(const socket_t socket) +{ + const auto it = this->socket_handlers.find(socket); + if (it == this->socket_handlers.end()) + throw std::runtime_error("Trying to remove a SocketHandler that is not managed"); + this->socket_handlers.erase(it); + +#if POLLER == POLL + for (size_t i = 0; i < this->nfds; i++) + { + if (this->fds[i].fd == socket) + { + // Move all subsequent pollfd by one on the left, erasing the + // value of the one we remove + for (size_t j = i; j < this->nfds - 1; ++j) + { + this->fds[j].fd = this->fds[j+1].fd; + this->fds[j].events= this->fds[j+1].events; + } + this->nfds--; + } + } +#elif POLLER == EPOLL + const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_DEL, socket, nullptr); + if (res == -1) + { + log_error("epoll_ctl failed: " << strerror(errno)); + throw std::runtime_error("Could not remove socket from epoll"); + } +#endif +} + +void Poller::watch_send_events(SocketHandler* socket_handler) +{ +#if POLLER == POLL + for (size_t i = 0; i <= this->nfds; ++i) + { + if (this->fds[i].fd == socket_handler->get_socket()) + { + this->fds[i].events = POLLIN|POLLOUT; + return; + } + } + throw std::runtime_error("Cannot watch a non-registered socket for send events"); +#elif POLLER == EPOLL + struct epoll_event event = {EPOLLIN|EPOLLOUT, {socket_handler}}; + const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event); + if (res == -1) + { + log_error("epoll_ctl failed: " << strerror(errno)); + throw std::runtime_error("Could not modify socket flags in epoll"); + } +#endif +} + +void Poller::stop_watching_send_events(SocketHandler* socket_handler) +{ +#if POLLER == POLL + for (size_t i = 0; i <= this->nfds; ++i) + { + if (this->fds[i].fd == socket_handler->get_socket()) + { + this->fds[i].events = POLLIN; + return; + } + } + throw std::runtime_error("Cannot watch a non-registered socket for send events"); +#elif POLLER == EPOLL + struct epoll_event event = {EPOLLIN, {socket_handler}}; + const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event); + if (res == -1) + { + log_error("epoll_ctl failed: " << strerror(errno)); + throw std::runtime_error("Could not modify socket flags in epoll"); + } +#endif +} + +int Poller::poll(const std::chrono::milliseconds& timeout) +{ + if (this->socket_handlers.empty() && timeout == utils::no_timeout) + return -1; +#if POLLER == POLL + // Convert our nice timeout into this ugly struct + struct timespec timeout_ts; + struct timespec* timeout_tsp; + if (timeout > 0s) + { + auto seconds = std::chrono::duration_cast(timeout); + timeout_ts.tv_sec = seconds.count(); + timeout_ts.tv_nsec = std::chrono::duration_cast(timeout - seconds).count(); + timeout_tsp = &timeout_ts; + } + else + timeout_tsp = nullptr; + + // Unblock all signals, only during the ppoll call + sigset_t empty_signal_set; + sigemptyset(&empty_signal_set); + int nb_events = ::ppoll(this->fds, this->nfds, timeout_tsp, + &empty_signal_set); + if (nb_events < 0) + { + if (errno == EINTR) + return true; + log_error("poll failed: " << strerror(errno)); + throw std::runtime_error("Poll failed"); + } + // We cannot possibly have more ready events than the number of fds we are + // watching + assert(static_cast(nb_events) <= this->nfds); + for (size_t i = 0; i <= this->nfds && nb_events != 0; ++i) + { + if (this->fds[i].revents == 0) + continue; + else if (this->fds[i].revents & POLLIN) + { + auto socket_handler = this->socket_handlers.at(this->fds[i].fd); + socket_handler->on_recv(); + nb_events--; + } + else if (this->fds[i].revents & POLLOUT) + { + auto socket_handler = this->socket_handlers.at(this->fds[i].fd); + if (socket_handler->is_connected()) + socket_handler->on_send(); + else + socket_handler->connect(); + nb_events--; + } + } + return 1; +#elif POLLER == EPOLL + static const size_t max_events = 12; + struct epoll_event revents[max_events]; + // Unblock all signals, only during the epoll_pwait call + sigset_t empty_signal_set; + sigemptyset(&empty_signal_set); + const int nb_events = ::epoll_pwait(this->epfd, revents, max_events, timeout.count(), + &empty_signal_set); + if (nb_events == -1) + { + if (errno == EINTR) + return 0; + log_error("epoll wait: " << strerror(errno)); + throw std::runtime_error("Epoll_wait failed"); + } + for (int i = 0; i < nb_events; ++i) + { + auto socket_handler = static_cast(revents[i].data.ptr); + if (revents[i].events & EPOLLIN) + socket_handler->on_recv(); + else if (revents[i].events & EPOLLOUT) + { + if (socket_handler->is_connected()) + socket_handler->on_send(); + else + socket_handler->connect(); + } + } + return nb_events; +#endif +} + +size_t Poller::size() const +{ + return this->socket_handlers.size(); +} diff --git a/louloulibs/network/poller.hpp b/louloulibs/network/poller.hpp new file mode 100644 index 0000000..de0cb48 --- /dev/null +++ b/louloulibs/network/poller.hpp @@ -0,0 +1,95 @@ +#ifndef POLLER_INCLUDED +# define POLLER_INCLUDED + +#include + +#include +#include +#include + +#define POLL 1 +#define EPOLL 2 +#define KQUEUE 3 +#include +#ifndef POLLER + #define POLLER POLL +#endif + +#if POLLER == POLL + #include + #define MAX_POLL_FD_NUMBER 4096 +#elif POLLER == EPOLL + #include +#else + #error Invalid POLLER value +#endif + +/** + * We pass some SocketHandlers to this Poller, which uses + * poll/epoll/kqueue/select etc to wait for events on these SocketHandlers, + * and call the callbacks when event occurs. + * + * TODO: support these pollers: + * - kqueue(2) + */ + +class Poller +{ +public: + explicit Poller(); + ~Poller(); + /** + * Add a SocketHandler to be monitored by this Poller. All receive events + * are always automatically watched. + */ + void add_socket_handler(SocketHandler* socket_handler); + /** + * Remove (and stop managing) a SocketHandler, designated by the given socket_t. + */ + void remove_socket_handler(const socket_t socket); + /** + * Signal the poller that he needs to watch for send events for the given + * SocketHandler. + */ + void watch_send_events(SocketHandler* socket_handler); + /** + * Signal the poller that he needs to stop watching for send events for + * this SocketHandler. + */ + void stop_watching_send_events(SocketHandler* socket_handler); + /** + * Wait for all watched events, and call the SocketHandlers' callbacks + * when one is ready. Returns if nothing happened before the provided + * timeout. If the timeout is 0, it waits forever. If there is no + * watched event, returns -1 immediately, ignoring the timeout value. + * Otherwise, returns the number of event handled. If 0 is returned this + * means that we were interrupted by a signal, or the timeout occured. + */ + int poll(const std::chrono::milliseconds& timeout); + /** + * Returns the number of SocketHandlers managed by the poller. + */ + size_t size() const; + +private: + /** + * A "list" of all the SocketHandlers that we manage, indexed by socket, + * because that's what is returned by select/poll/etc when an event + * occures. + */ + std::unordered_map socket_handlers; + +#if POLLER == POLL + struct pollfd fds[MAX_POLL_FD_NUMBER]; + nfds_t nfds; +#elif POLLER == EPOLL + int epfd; +#endif + + Poller(const Poller&) = delete; + Poller(Poller&&) = delete; + Poller& operator=(const Poller&) = delete; + Poller& operator=(Poller&&) = delete; +}; + +#endif // POLLER_INCLUDED diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp new file mode 100644 index 0000000..d01ac5d --- /dev/null +++ b/louloulibs/network/socket_handler.hpp @@ -0,0 +1,45 @@ +#ifndef SOCKET_HANDLER_HPP +# define SOCKET_HANDLER_HPP + +#include +#include + +class Poller; + +typedef int socket_t; + +class SocketHandler +{ +public: + explicit SocketHandler(std::shared_ptr poller, const socket_t socket): + poller(poller), + socket(socket) + {} + virtual ~SocketHandler() {} + virtual void on_recv() = 0; + virtual void on_send() = 0; + virtual void connect() = 0; + virtual bool is_connected() const = 0; + + socket_t get_socket() const + { return this->socket; } + +protected: + /** + * A pointer to the poller that manages us, because we need to communicate + * with it. + */ + std::shared_ptr poller; + /** + * The handled socket. + */ + socket_t socket; + +private: + SocketHandler(const SocketHandler&) = delete; + SocketHandler(SocketHandler&&) = delete; + SocketHandler& operator=(const SocketHandler&) = delete; + SocketHandler& operator=(SocketHandler&&) = delete; +}; + +#endif // SOCKET_HANDLER_HPP diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp new file mode 100644 index 0000000..f647b86 --- /dev/null +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -0,0 +1,590 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef BOTAN_FOUND +# include + +Botan::AutoSeeded_RNG TCPSocketHandler::rng; +Permissive_Credentials_Manager TCPSocketHandler::credential_manager; +Botan::TLS::Policy TCPSocketHandler::policy; +Botan::TLS::Session_Manager_In_Memory TCPSocketHandler::session_manager(TCPSocketHandler::rng); + +#endif + +#ifndef UIO_FASTIOV +# define UIO_FASTIOV 8 +#endif + +using namespace std::string_literals; +using namespace std::chrono_literals; + +namespace ph = std::placeholders; + +TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): + SocketHandler(poller, -1), + use_tls(false), + connected(false), + connecting(false) +#ifdef CARES_FOUND + ,resolving(false), + resolved(false), + resolved4(false), + resolved6(false), + cares_addrinfo(nullptr), + cares_error() +#endif +{} + +TCPSocketHandler::~TCPSocketHandler() +{ +#ifdef CARES_FOUND + this->free_cares_addrinfo(); +#endif +} + +void TCPSocketHandler::init_socket(const struct addrinfo* rp) +{ + if ((this->socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) + throw std::runtime_error("Could not create socket: "s + strerror(errno)); + int optval = 1; + if (::setsockopt(this->socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) + log_warning("Failed to enable TCP keepalive on socket: " << strerror(errno)); + // Set the socket on non-blocking mode. This is useful to receive a EAGAIN + // error when connect() would block, to not block the whole process if a + // remote is not responsive. + const int existing_flags = ::fcntl(this->socket, F_GETFL, 0); + if ((existing_flags == -1) || + (::fcntl(this->socket, F_SETFL, existing_flags | O_NONBLOCK) == -1)) + throw std::runtime_error("Could not initialize socket: "s + strerror(errno)); +} + +void TCPSocketHandler::connect(const std::string& address, const std::string& port, const bool tls) +{ + this->address = address; + this->port = port; + this->use_tls = tls; + + utils::ScopeGuard sg; + + struct addrinfo* addr_res; + + if (!this->connecting) + { + // Get the addrinfo from getaddrinfo (or ares_gethostbyname), only if + // this is the first call of this function. +#ifdef CARES_FOUND + if (!this->resolved) + { + log_info("Trying to connect to " << address << ":" << port); + // Start the asynchronous process of resolving the hostname. Once + // the addresses have been found and `resolved` has been set to true + // (but connecting will still be false), TCPSocketHandler::connect() + // needs to be called, again. + this->resolving = true; + DNSHandler::instance.gethostbyname(address, this, AF_INET6); + DNSHandler::instance.gethostbyname(address, this, AF_INET); + return; + } + else + { + // The c-ares resolved the hostname and the available addresses + // where saved in the cares_addrinfo linked list. Now, just use + // this list to try to connect. + addr_res = this->cares_addrinfo; + if (!addr_res) + { + this->close(); + this->on_connection_failed(this->cares_error); + return ; + } + } +#else + log_info("Trying to connect to " << address << ":" << port); + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + const int res = ::getaddrinfo(address.c_str(), port.c_str(), &hints, &addr_res); + + if (res != 0) + { + log_warning("getaddrinfo failed: "s + gai_strerror(res)); + this->close(); + this->on_connection_failed(gai_strerror(res)); + return ; + } + // Make sure the alloced structure is always freed at the end of the + // function + sg.add_callback([&addr_res](){ freeaddrinfo(addr_res); }); +#endif + } + else + { // This function is called again, use the saved addrinfo structure, + // instead of re-doing the whole getaddrinfo process. + addr_res = &this->addrinfo; + } + + for (struct addrinfo* rp = addr_res; rp; rp = rp->ai_next) + { + if (!this->connecting) + { + try { + this->init_socket(rp); + } + catch (const std::runtime_error& error) { + log_error("Failed to init socket: " << error.what()); + break; + } + } + if (::connect(this->socket, rp->ai_addr, rp->ai_addrlen) == 0 + || errno == EISCONN) + { + log_info("Connection success."); + TimedEventsManager::instance().cancel("connection_timeout"s + + std::to_string(this->socket)); + this->poller->add_socket_handler(this); + this->connected = true; + this->connecting = false; +#ifdef BOTAN_FOUND + if (this->use_tls) + this->start_tls(); +#endif + this->on_connected(); + return ; + } + else if (errno == EINPROGRESS || errno == EALREADY) + { // retry this process later, when the socket + // is ready to be written on. + this->connecting = true; + this->poller->add_socket_handler(this); + this->poller->watch_send_events(this); + // Save the addrinfo structure, to use it on the next call + this->ai_addrlen = rp->ai_addrlen; + memcpy(&this->ai_addr, rp->ai_addr, this->ai_addrlen); + memcpy(&this->addrinfo, rp, sizeof(struct addrinfo)); + this->addrinfo.ai_addr = reinterpret_cast(&this->ai_addr); + this->addrinfo.ai_next = nullptr; + // If the connection has not succeeded or failed in 5s, we consider + // it to have failed + TimedEventsManager::instance().add_event( + TimedEvent(std::chrono::steady_clock::now() + 5s, + std::bind(&TCPSocketHandler::on_connection_timeout, this), + "connection_timeout"s + std::to_string(this->socket))); + return ; + } + log_info("Connection failed:" << strerror(errno)); + } + log_error("All connection attempts failed."); + this->close(); + this->on_connection_failed(strerror(errno)); + return ; +} + +void TCPSocketHandler::on_connection_timeout() +{ + this->close(); + this->on_connection_failed("connection timed out"); +} + +void TCPSocketHandler::connect() +{ + this->connect(this->address, this->port, this->use_tls); +} + +void TCPSocketHandler::on_recv() +{ +#ifdef BOTAN_FOUND + if (this->use_tls) + this->tls_recv(); + else +#endif + this->plain_recv(); +} + +void TCPSocketHandler::plain_recv() +{ + static constexpr size_t buf_size = 4096; + char buf[buf_size]; + void* recv_buf = this->get_receive_buffer(buf_size); + + if (recv_buf == nullptr) + recv_buf = buf; + + const ssize_t size = this->do_recv(recv_buf, buf_size); + + if (size > 0) + { + if (buf == recv_buf) + { + // data needs to be placed in the in_buf string, because no buffer + // was provided to receive that data directly. The in_buf buffer + // will be handled in parse_in_buffer() + this->in_buf += std::string(buf, size); + } + this->parse_in_buffer(size); + } +} + +ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size) +{ + ssize_t size = ::recv(this->socket, recv_buf, buf_size, 0); + if (0 == size) + { + this->on_connection_close(""); + this->close(); + } + else if (-1 == size) + { + log_warning("Error while reading from socket: " << strerror(errno)); + // Remember if we were connecting, or already connected when this + // happened, because close() sets this->connecting to false + const auto were_connecting = this->connecting; + this->close(); + if (were_connecting) + this->on_connection_failed(strerror(errno)); + else + this->on_connection_close(strerror(errno)); + } + return size; +} + +void TCPSocketHandler::on_send() +{ + struct iovec msg_iov[UIO_FASTIOV] = {}; + struct msghdr msg{nullptr, 0, + msg_iov, + 0, nullptr, 0, 0}; + for (std::string& s: this->out_buf) + { + // unconsting the content of s is ok, sendmsg will never modify it + msg_iov[msg.msg_iovlen].iov_base = const_cast(s.data()); + msg_iov[msg.msg_iovlen].iov_len = s.size(); + if (++msg.msg_iovlen == UIO_FASTIOV) + break; + } + ssize_t res = ::sendmsg(this->socket, &msg, MSG_NOSIGNAL); + if (res < 0) + { + log_error("sendmsg failed: " << strerror(errno)); + this->on_connection_close(strerror(errno)); + this->close(); + } + else + { + // remove all the strings that were successfully sent. + for (auto it = this->out_buf.begin(); + it != this->out_buf.end();) + { + if (static_cast(res) >= (*it).size()) + { + res -= (*it).size(); + it = this->out_buf.erase(it); + } + else + { + // If one string has partially been sent, we use substr to + // crop it + if (res > 0) + (*it) = (*it).substr(res, std::string::npos); + break; + } + } + if (this->out_buf.empty()) + this->poller->stop_watching_send_events(this); + } +} + +void TCPSocketHandler::close() +{ + TimedEventsManager::instance().cancel("connection_timeout"s + + std::to_string(this->socket)); + if (this->connected || this->connecting) + this->poller->remove_socket_handler(this->get_socket()); + if (this->socket != -1) + { + ::close(this->socket); + this->socket = -1; + } + this->connected = false; + this->connecting = false; +#ifdef CARES_FOUND + this->resolving = false; + this->resolved = false; + this->resolved4 = false; + this->resolved6 = false; + this->free_cares_addrinfo(); + this->cares_error.clear(); +#endif + this->in_buf.clear(); + this->out_buf.clear(); + this->port.clear(); +} + +void TCPSocketHandler::send_data(std::string&& data) +{ +#ifdef BOTAN_FOUND + if (this->use_tls) + this->tls_send(std::move(data)); + else +#endif + this->raw_send(std::move(data)); +} + +void TCPSocketHandler::raw_send(std::string&& data) +{ + if (data.empty()) + return ; + this->out_buf.emplace_back(std::move(data)); + if (this->connected) + this->poller->watch_send_events(this); +} + +void TCPSocketHandler::send_pending_data() +{ + if (this->connected && !this->out_buf.empty()) + this->poller->watch_send_events(this); +} + +bool TCPSocketHandler::is_connected() const +{ + return this->connected; +} + +bool TCPSocketHandler::is_connecting() const +{ +#ifdef CARES_FOUND + return this->connecting || this->resolving; +#else + return this->connecting; +#endif +} + +void* TCPSocketHandler::get_receive_buffer(const size_t) const +{ + return nullptr; +} + +#ifdef BOTAN_FOUND +void TCPSocketHandler::start_tls() +{ + Botan::TLS::Server_Information server_info(this->address, "irc", std::stoul(this->port)); + this->tls = std::make_unique( + std::bind(&TCPSocketHandler::tls_output_fn, this, ph::_1, ph::_2), + std::bind(&TCPSocketHandler::tls_data_cb, this, ph::_1, ph::_2), + std::bind(&TCPSocketHandler::tls_alert_cb, this, ph::_1, ph::_2, ph::_3), + std::bind(&TCPSocketHandler::tls_handshake_cb, this, ph::_1), + session_manager, credential_manager, policy, + rng, server_info, Botan::TLS::Protocol_Version::latest_tls_version()); +} + +void TCPSocketHandler::tls_recv() +{ + static constexpr size_t buf_size = 4096; + char recv_buf[buf_size]; + + const ssize_t size = this->do_recv(recv_buf, buf_size); + if (size > 0) + { + const bool was_active = this->tls->is_active(); + this->tls->received_data(reinterpret_cast(recv_buf), + static_cast(size)); + if (!was_active && this->tls->is_active()) + this->on_tls_activated(); + } +} + +void TCPSocketHandler::tls_send(std::string&& data) +{ + if (this->tls->is_active()) + { + const bool was_active = this->tls->is_active(); + if (!this->pre_buf.empty()) + { + this->tls->send(reinterpret_cast(this->pre_buf.data()), + this->pre_buf.size()); + this->pre_buf = ""; + } + if (!data.empty()) + this->tls->send(reinterpret_cast(data.data()), + data.size()); + if (!was_active && this->tls->is_active()) + this->on_tls_activated(); + } + else + this->pre_buf += data; +} + +void TCPSocketHandler::tls_data_cb(const Botan::byte* data, size_t size) +{ + this->in_buf += std::string(reinterpret_cast(data), + size); + if (!this->in_buf.empty()) + this->parse_in_buffer(size); +} + +void TCPSocketHandler::tls_output_fn(const Botan::byte* data, size_t size) +{ + this->raw_send(std::string(reinterpret_cast(data), size)); +} + +void TCPSocketHandler::tls_alert_cb(Botan::TLS::Alert alert, const Botan::byte*, size_t) +{ + log_debug("tls_alert: " << alert.type_string()); +} + +bool TCPSocketHandler::tls_handshake_cb(const Botan::TLS::Session& session) +{ + log_debug("Handshake with " << session.server_info().hostname() << " complete." + << " Version: " << session.version().to_string() + << " using " << session.ciphersuite().to_string()); + if (!session.session_id().empty()) + log_debug("Session ID " << Botan::hex_encode(session.session_id())); + if (!session.session_ticket().empty()) + log_debug("Session ticket " << Botan::hex_encode(session.session_ticket())); + return true; +} + +void TCPSocketHandler::on_tls_activated() +{ + this->send_data(""); +} + +void Permissive_Credentials_Manager::verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector&) +{ // TODO: Offer the admin to disallow connection on untrusted + // certificates + log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); +} + +#endif // BOTAN_FOUND + +#ifdef CARES_FOUND + +void TCPSocketHandler::on_hostname4_resolved(int status, struct hostent* hostent) +{ + this->resolved4 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo4(hostent); + else + this->cares_error = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + { + this->resolved = true; + this->resolving = false; + this->connect(); + } +} + +void TCPSocketHandler::on_hostname6_resolved(int status, struct hostent* hostent) +{ + this->resolved6 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo6(hostent); + else + this->cares_error = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + { + this->resolved = true; + this->resolving = false; + this->connect(); + } +} + +void TCPSocketHandler::fill_ares_addrinfo4(const struct hostent* hostent) +{ + struct addrinfo* prev = this->cares_addrinfo; + struct in_addr** address = reinterpret_cast(hostent->h_addr_list); + + while (*address) + { + // Create a new addrinfo list element, and fill it + struct addrinfo* current = new struct addrinfo; + current->ai_flags = 0; + current->ai_family = hostent->h_addrtype; + current->ai_socktype = SOCK_STREAM; + current->ai_protocol = 0; + current->ai_addrlen = sizeof(struct sockaddr_in); + + struct sockaddr_in* addr = new struct sockaddr_in; + addr->sin_family = hostent->h_addrtype; + addr->sin_port = htons(strtoul(this->port.data(), nullptr, 10)); + addr->sin_addr.s_addr = (*address)->s_addr; + + current->ai_addr = reinterpret_cast(addr); + current->ai_next = nullptr; + current->ai_canonname = nullptr; + + current->ai_next = prev; + this->cares_addrinfo = current; + prev = current; + ++address; + } +} + +void TCPSocketHandler::fill_ares_addrinfo6(const struct hostent* hostent) +{ + struct addrinfo* prev = this->cares_addrinfo; + struct in6_addr** address = reinterpret_cast(hostent->h_addr_list); + + while (*address) + { + // Create a new addrinfo list element, and fill it + struct addrinfo* current = new struct addrinfo; + current->ai_flags = 0; + current->ai_family = hostent->h_addrtype; + current->ai_socktype = SOCK_STREAM; + current->ai_protocol = 0; + current->ai_addrlen = sizeof(struct sockaddr_in6); + + struct sockaddr_in6* addr = new struct sockaddr_in6; + addr->sin6_family = hostent->h_addrtype; + addr->sin6_port = htons(strtoul(this->port.data(), nullptr, 10)); + ::memcpy(addr->sin6_addr.s6_addr, (*address)->s6_addr, 16); + addr->sin6_flowinfo = 0; + addr->sin6_scope_id = 0; + + current->ai_addr = reinterpret_cast(addr); + current->ai_next = nullptr; + current->ai_canonname = nullptr; + + current->ai_next = prev; + this->cares_addrinfo = current; + prev = current; + ++address; + } +} + +void TCPSocketHandler::free_cares_addrinfo() +{ + while (this->cares_addrinfo) + { + delete this->cares_addrinfo->ai_addr; + auto next = this->cares_addrinfo->ai_next; + delete this->cares_addrinfo; + this->cares_addrinfo = next; + } +} + +#endif // CARES_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp new file mode 100644 index 0000000..2b5cd49 --- /dev/null +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -0,0 +1,293 @@ +#ifndef SOCKET_HANDLER_INCLUDED +# define SOCKET_HANDLER_INCLUDED + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "louloulibs.h" + +#ifdef CARES_FOUND +# include +#endif + +#ifdef BOTAN_FOUND +# include +# include + +/** + * A very simple credential manager that accepts any certificate. + */ +class Permissive_Credentials_Manager: public Botan::Credentials_Manager +{ +public: + void verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector&); +}; +#endif // BOTAN_FOUND + +/** + * An interface, with a series of callbacks that should be implemented in + * subclasses that deal with a socket. These callbacks are called on various events + * (read/write/timeout, etc) when they are notified to a poller + * (select/poll/epoll etc) + */ +class TCPSocketHandler: public SocketHandler +{ +protected: + ~TCPSocketHandler(); + +public: + explicit TCPSocketHandler(std::shared_ptr poller); + /** + * Connect to the remote server, and call on_connected() if this + * succeeds. If tls is true, we set use_tls to true and will also call + * start_tls() when the connection succeeds. + */ + void connect(const std::string& address, const std::string& port, const bool tls); + void connect() override final; + /** + * Reads raw data from the socket. And pass it to parse_in_buffer() + * If we are using TLS on this connection, we call tls_recv() + */ + void on_recv() override final; + /** + * Write as much data from out_buf as possible, in the socket. + */ + void on_send() override final; + /** + * Add the given data to out_buf and tell our poller that we want to be + * notified when a send event is ready. + * + * This can be overriden if we want to modify the data before sending + * it. For example if we want to encrypt it. + */ + void send_data(std::string&& data); + /** + * Watch the socket for send events, if our out buffer is not empty. + */ + void send_pending_data(); + /** + * Close the connection, remove us from the poller + */ + void close(); + /** + * Called by a TimedEvent, when the connection did not succeed or fail + * after a given time. + */ + void on_connection_timeout(); + /** + * Called when the connection is successful. + */ + virtual void on_connected() = 0; + /** + * Called when the connection fails. Not when it is closed later, just at + * the connect() call. + */ + virtual void on_connection_failed(const std::string& reason) = 0; + /** + * Called when we detect a disconnection from the remote host. + */ + virtual void on_connection_close(const std::string& error) = 0; + /** + * Handle/consume (some of) the data received so far. The data to handle + * may be in the in_buf buffer, or somewhere else, depending on what + * get_receive_buffer() returned. If some data is used from in_buf, it + * should be truncated, only the unused data should be left untouched. + * + * The size argument is the size of the last chunk of data that was added to the buffer. + */ + virtual void parse_in_buffer(const size_t size) = 0; + bool is_connected() const override final; + bool is_connecting() const; + +#ifdef CARES_FOUND + void on_hostname4_resolved(int status, struct hostent* hostent); + void on_hostname6_resolved(int status, struct hostent* hostent); + + void free_cares_addrinfo(); + + void fill_ares_addrinfo4(const struct hostent* hostent); + void fill_ares_addrinfo6(const struct hostent* hostent); +#endif + +private: + /** + * Initialize the socket with the parameters contained in the given + * addrinfo structure. + */ + void init_socket(const struct addrinfo* rp); + /** + * Reads from the socket into the provided buffer. If an error occurs + * (read returns <= 0), the handling of the error is done here (close the + * connection, log a message, etc). + * + * Returns the value returned by ::recv(), so the buffer should not be + * used if it’s not positive. + */ + ssize_t do_recv(void* recv_buf, const size_t buf_size); + /** + * Reads data from the socket and calls parse_in_buffer with it. + */ + void plain_recv(); + /** + * Mark the given data as ready to be sent, as-is, on the socket, as soon + * as we can. + */ + void raw_send(std::string&& data); + +#ifdef BOTAN_FOUND + /** + * Create the TLS::Client object, with all the callbacks etc. This must be + * called only when we know we are able to send TLS-encrypted data over + * the socket. + */ + void start_tls(); + /** + * An additional step to pass the data into our tls object to decrypt it + * before passing it to parse_in_buffer. + */ + void tls_recv(); + /** + * Pass the data to the tls object in order to encrypt it. The tls object + * will then call raw_send as a callback whenever data as been encrypted + * and can be sent on the socket. + */ + void tls_send(std::string&& data); + /** + * Called by the tls object that some data has been decrypt. We call + * parse_in_buffer() to handle that unencrypted data. + */ + void tls_data_cb(const Botan::byte* data, size_t size); + /** + * Called by the tls object to indicate that some data has been encrypted + * and is now ready to be sent on the socket as is. + */ + void tls_output_fn(const Botan::byte* data, size_t size); + /** + * Called by the tls object to indicate that a TLS alert has been + * received. We don’t use it, we just log some message, at the moment. + */ + void tls_alert_cb(Botan::TLS::Alert alert, const Botan::byte*, size_t); + /** + * Called by the tls object at the end of the TLS handshake. We don't do + * anything here appart from logging the TLS session information. + */ + bool tls_handshake_cb(const Botan::TLS::Session& session); + /** + * Called whenever the tls session goes from inactive to active. This + * means that the handshake has just been successfully done, and we can + * now proceed to send any available data into our tls object. + */ + void on_tls_activated(); +#endif // BOTAN_FOUND + /** + * Where data is added, when we want to send something to the client. + */ + std::list out_buf; + /** + * Keep the details of the addrinfo that triggered a EINPROGRESS error when + * connect()ing to it, to reuse it directly when connect() is called + * again. + */ + struct addrinfo addrinfo; + struct sockaddr_in6 ai_addr; + socklen_t ai_addrlen; + +protected: + /** + * Where data read from the socket is added until we can extract a full + * and meaningful “message” from it. + * + * TODO: something more efficient than a string. + */ + std::string in_buf; + /** + * Whether we are using TLS on this connection or not. + */ + bool use_tls; + /** + * Provide a buffer in which data can be directly received. This can be + * used to avoid copying data into in_buf before using it. If no buffer + * needs to be provided, nullptr is returned (the default implementation + * does that), in that case our internal in_buf will be used to save the + * data until it can be used by parse_in_buffer(). + */ + virtual void* get_receive_buffer(const size_t size) const; + /** + * Hostname we are connected/connecting to + */ + std::string address; + /** + * Port we are connected/connecting to + */ + std::string port; + + bool connected; + bool connecting; + +#ifdef CARES_FOUND + bool resolving; + /** + * Whether or not the DNS resolution was successfully done + */ + bool resolved; + bool resolved4; + bool resolved6; + /** + * When using c-ares to resolve the host asynchronously, we need the + * c-ares callback to fill a structure (a struct addrinfo, for + * compatibility with getaddrinfo and the rest of the code that works when + * c-ares is not used) with all returned values (for example an IPv6 and + * an IPv4). The next call of connect() will then try all these values + * (exactly like we do with the result of getaddrinfo) and save the one + * that worked (or returned EINPROGRESS) in the other struct addrinfo (see + * the members addrinfo, ai_addrlen, and ai_addr). + */ + struct addrinfo* cares_addrinfo; + std::string cares_error; +#endif // CARES_FOUND + +private: + TCPSocketHandler(const TCPSocketHandler&) = delete; + TCPSocketHandler(TCPSocketHandler&&) = delete; + TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; + TCPSocketHandler& operator=(TCPSocketHandler&&) = delete; + +#ifdef BOTAN_FOUND + /** + * Botan stuff to manipulate a TLS session. + */ + static Botan::AutoSeeded_RNG rng; + static Permissive_Credentials_Manager credential_manager; + static Botan::TLS::Policy policy; + static Botan::TLS::Session_Manager_In_Memory session_manager; + /** + * We use a unique_ptr because we may not want to create the object at + * all. The Botan::TLS::Client object generates a handshake message as + * soon and calls the output_fn callback with it as soon as it is + * created. Therefore, we do not want to create it if do not intend to do + * send any TLS-encrypted message. We create the object only when needed + * (for example after we have negociated a TLS session using a STARTTLS + * message, or stuf like that). + * + * See start_tls for the method where this object is created. + */ + std::unique_ptr tls; + /** + * An additional buffer to keep data that the user wants to send, but + * cannot because the handshake is not done. + */ + std::string pre_buf; +#endif // BOTAN_FOUND +}; + +#endif // SOCKET_HANDLER_INCLUDED diff --git a/louloulibs/utils/encoding.cpp b/louloulibs/utils/encoding.cpp new file mode 100644 index 0000000..f738ce2 --- /dev/null +++ b/louloulibs/utils/encoding.cpp @@ -0,0 +1,254 @@ +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +/** + * The UTF-8-encoded character used as a place holder when a character conversion fails. + * This is U+FFFD � "replacement character" + */ +static const char* invalid_char = "\xef\xbf\xbd"; +static const size_t invalid_char_len = 3; + +namespace utils +{ + /** + * Based on http://en.wikipedia.org/wiki/UTF-8#Description + */ + bool is_valid_utf8(const char* s) + { + if (!s) + return false; + + const unsigned char* str = reinterpret_cast(s); + + while (*str) + { + // 4 bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + if ((str[0] & 0b11111000) == 0b11110000) + { + if (!str[1] || !str[2] || !str[3] + || ((str[1] & 0b11000000) != 0b10000000) + || ((str[2] & 0b11000000) != 0b10000000) + || ((str[3] & 0b11000000) != 0b10000000)) + return false; + str += 4; + } + // 3 bytes: 1110xxx 10xxxxxx 10xxxxxx + else if ((str[0] & 0b11110000) == 0b11100000) + { + if (!str[1] || !str[2] + || ((str[1] & 0b11000000) != 0b10000000) + || ((str[2] & 0b11000000) != 0b10000000)) + return false; + str += 3; + } + // 2 bytes: 110xxxxx 10xxxxxx + else if (((str[0]) & 0b11100000) == 0b11000000) + { + if (!str[1] || + ((str[1] & 0b11000000) != 0b10000000)) + return false; + str += 2; + } + // 1 byte: 0xxxxxxx + else if ((str[0] & 0b10000000) != 0) + return false; + else + str++; + } + return true; + } + + std::string remove_invalid_xml_chars(const std::string& original) + { + // The given string MUST be a valid utf-8 string + unsigned char* res = new unsigned char[original.size()]; + ScopeGuard sg([&res]() { delete[] res;}); + + // pointer where we write valid chars + unsigned char* r = res; + + const unsigned char* str = reinterpret_cast(original.c_str()); + std::bitset<20> codepoint; + + while (*str) + { + // 4 bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + if ((str[0] & 0b11111000) == 0b11110000) + { + codepoint = ((str[0] & 0b00000111) << 18); + codepoint |= ((str[1] & 0b00111111) << 12); + codepoint |= ((str[2] & 0b00111111) << 6 ); + codepoint |= ((str[3] & 0b00111111) << 0 ); + if (codepoint.to_ulong() <= 0x10FFFF) + { + ::memcpy(r, str, 4); + r += 4; + } + str += 4; + } + // 3 bytes: 1110xxx 10xxxxxx 10xxxxxx + else if ((str[0] & 0b11110000) == 0b11100000) + { + codepoint = ((str[0] & 0b00001111) << 12); + codepoint |= ((str[1] & 0b00111111) << 6); + codepoint |= ((str[2] & 0b00111111) << 0 ); + if (codepoint.to_ulong() <= 0xD7FF || + (codepoint.to_ulong() >= 0xE000 && codepoint.to_ulong() <= 0xFFFD)) + { + ::memcpy(r, str, 3); + r += 3; + } + str += 3; + } + // 2 bytes: 110xxxxx 10xxxxxx + else if (((str[0]) & 0b11100000) == 0b11000000) + { + // All 2 bytes char are valid, don't even bother calculating + // the codepoint + ::memcpy(r, str, 2); + r += 2; + str += 2; + } + // 1 byte: 0xxxxxxx + else if ((str[0] & 0b10000000) == 0) + { + codepoint = ((str[0] & 0b01111111)); + if (codepoint.to_ulong() == 0x09 || + codepoint.to_ulong() == 0x0A || + codepoint.to_ulong() == 0x0D || + codepoint.to_ulong() >= 0x20) + { + ::memcpy(r, str, 1); + r += 1; + } + str += 1; + } + else + throw std::runtime_error("Invalid UTF-8 passed to remove_invalid_xml_chars"); + } + return std::string(reinterpret_cast(res), r-res); + } + + std::string convert_to_utf8(const std::string& str, const char* charset) + { + std::string res; + + const iconv_t cd = iconv_open("UTF-8", charset); + if (cd == (iconv_t)-1) + throw std::runtime_error("Cannot convert into UTF-8"); + + // Make sure cd is always closed when we leave this function + ScopeGuard sg([&]{ iconv_close(cd); }); + + size_t inbytesleft = str.size(); + + // iconv will not attempt to modify this buffer, but some plateform + // require a char** anyway +#ifdef ICONV_SECOND_ARGUMENT_IS_CONST + const char* inbuf_ptr = str.c_str(); +#else + char* inbuf_ptr = const_cast(str.c_str()); +#endif + + size_t outbytesleft = str.size() * 4; + char* outbuf = new char[outbytesleft]; + char* outbuf_ptr = outbuf; + + // Make sure outbuf is always deleted when we leave this function + sg.add_callback([&]{ delete[] outbuf; }); + + bool done = false; + while (done == false) + { + size_t error = iconv(cd, &inbuf_ptr, &inbytesleft, &outbuf_ptr, &outbytesleft); + if ((size_t)-1 == error) + { + switch (errno) + { + case EILSEQ: + // Invalid byte found. Insert a placeholder instead of the + // converted character, jump one byte and continue + memcpy(outbuf_ptr, invalid_char, invalid_char_len); + outbuf_ptr += invalid_char_len; + inbytesleft--; + inbuf_ptr++; + break; + case EINVAL: + // A multibyte sequence is not terminated, but we can't + // provide any more data, so we just add a placeholder to + // indicate that the character is not properly converted, + // and we stop the conversion + memcpy(outbuf_ptr, invalid_char, invalid_char_len); + outbuf_ptr += invalid_char_len; + outbuf_ptr++; + done = true; + break; + case E2BIG: + // This should never happen + done = true; + break; + default: + // This should happen even neverer + done = true; + break; + } + } + else + { + // The conversion finished without any error, stop converting + done = true; + } + } + // Terminate the converted buffer, and copy that buffer it into the + // string we return + *outbuf_ptr = '\0'; + res = outbuf; + return res; + } + +} + +namespace xep0106 +{ + static const std::map encode_map = { + {' ', "\\20"}, + {'"', "\\22"}, + {'&', "\\26"}, + {'\'',"\\27"}, + {'/', "\\2f"}, + {':', "\\3a"}, + {'<', "\\3c"}, + {'>', "\\3e"}, + {'@', "\\40"}, + }; + + void decode(std::string& s) + { + std::string::size_type pos; + for (const auto& pair: encode_map) + while ((pos = s.find(pair.second)) != std::string::npos) + s.replace(pos, pair.second.size(), + 1, pair.first); + } + + void encode(std::string& s) + { + std::string::size_type pos; + while ((pos = s.find_first_of(" \"&'/:<>@")) != std::string::npos) + { + auto it = encode_map.find(s[pos]); + assert(it != encode_map.end()); + s.replace(pos, 1, it->second); + } + } +} diff --git a/louloulibs/utils/encoding.hpp b/louloulibs/utils/encoding.hpp new file mode 100644 index 0000000..6b7ccd2 --- /dev/null +++ b/louloulibs/utils/encoding.hpp @@ -0,0 +1,38 @@ +#ifndef ENCODING_INCLUDED +# define ENCODING_INCLUDED + +#include + +namespace utils +{ + /** + * Returns true if the given null-terminated string is valid utf-8. + * + * Based on http://en.wikipedia.org/wiki/UTF-8#Description + */ + bool is_valid_utf8(const char* s); + /** + * Remove all invalid codepoints from the given utf-8-encoded string. + * The value returned is a copy of the string, without the removed chars. + * + * See http://www.w3.org/TR/xml/#charsets for the list of valid characters + * in XML. + */ + std::string remove_invalid_xml_chars(const std::string& original); + /** + * Convert the given string (encoded is "encoding") into valid utf-8. + * If some decoding fails, insert an utf-8 placeholder character instead. + */ + std::string convert_to_utf8(const std::string& str, const char* encoding); +} + +namespace xep0106 +{ + /** + * Decode and encode inplace. + */ + void decode(std::string&); + void encode(std::string&); +} + +#endif // ENCODING_INCLUDED diff --git a/louloulibs/utils/reload.cpp b/louloulibs/utils/reload.cpp new file mode 100644 index 0000000..6600c75 --- /dev/null +++ b/louloulibs/utils/reload.cpp @@ -0,0 +1,13 @@ +#include +#include + +void reload_process() +{ + // Closing the config will just force it to be reopened the next time + // a configuration option is needed + Config::close(); + // Destroy the logger instance, to be recreated the next time a log + // line needs to be written + Logger::instance().reset(); + log_debug("Configuration and logger reloaded."); +} diff --git a/louloulibs/utils/reload.hpp b/louloulibs/utils/reload.hpp new file mode 100644 index 0000000..16d64f7 --- /dev/null +++ b/louloulibs/utils/reload.hpp @@ -0,0 +1,10 @@ +#ifndef RELOAD_HPP_INCLUDED +#define RELOAD_HPP_INCLUDED + +/** + * Reload the server's configuration, and close the logger (so that it + * closes its files etc, to take into account the new configuration) + */ +void reload_process(); + +#endif /* RELOAD_HPP_INCLUDED */ diff --git a/louloulibs/utils/revstr.cpp b/louloulibs/utils/revstr.cpp new file mode 100644 index 0000000..87fd801 --- /dev/null +++ b/louloulibs/utils/revstr.cpp @@ -0,0 +1,9 @@ +#include + +namespace utils +{ + std::string revstr(const std::string& original) + { + return {original.rbegin(), original.rend()}; + } +} diff --git a/louloulibs/utils/revstr.hpp b/louloulibs/utils/revstr.hpp new file mode 100644 index 0000000..27c9e3e --- /dev/null +++ b/louloulibs/utils/revstr.hpp @@ -0,0 +1,11 @@ +#ifndef REVSTR_HPP_INCLUDED +# define REVSTR_HPP_INCLUDED + +#include + +namespace utils +{ + std::string revstr(const std::string& original); +} + +#endif // REVSTR_HPP_INCLUDED diff --git a/louloulibs/utils/scopeguard.hpp b/louloulibs/utils/scopeguard.hpp new file mode 100644 index 0000000..df78831 --- /dev/null +++ b/louloulibs/utils/scopeguard.hpp @@ -0,0 +1,89 @@ +#ifndef SCOPEGUARD_HPP +#define SCOPEGUARD_HPP + +#include +#include + +/** + * A class to be used to make sure some functions are called when the scope + * is left, because they will be called in the ScopeGuard's destructor. It + * can for example be used to delete some pointer whenever any exception is + * called. Example: + + * { + * ScopeGuard scope; + * int* number = new int(2); + * scope.add_callback([number]() { delete number; }); + * // Do some other stuff with the number. But these stuff might throw an exception: + * throw std::runtime_error("Some error not caught here, but in our caller"); + * return true; + * } + + * In this example, our pointer will always be deleted, even when the + * exception is thrown. If we want the functions to be called only when the + * scope is left because of an unexpected exception, we can use + * ScopeGuard::disable(); + */ + +namespace utils +{ + +class ScopeGuard +{ +public: + /** + * The constructor can take a callback. But additional callbacks can be + * added later with add_callback() + */ + explicit ScopeGuard(std::function&& func): + enabled(true) + { + this->add_callback(std::move(func)); + } + /** + * default constructor, the scope guard is enabled but empty, use + * add_callback() + */ + explicit ScopeGuard(): + enabled(true) + { + } + /** + * Call all callbacks in the desctructor, unless it has been disabled. + */ + ~ScopeGuard() + { + if (this->enabled) + for (auto& func: this->callbacks) + func(); + } + /** + * Add a callback to be called in our destructor, one scope guard can be + * used for more than one task, if needed. + */ + void add_callback(std::function&& func) + { + this->callbacks.emplace_back(std::move(func)); + } + /** + * Disable that scope guard, nothing will be done when the scope is + * exited. + */ + void disable() + { + this->enabled = false; + } + +private: + bool enabled; + std::vector> callbacks; + + ScopeGuard(const ScopeGuard&) = delete; + ScopeGuard& operator=(ScopeGuard&&) = delete; + ScopeGuard(ScopeGuard&&) = delete; + ScopeGuard& operator=(const ScopeGuard&) = delete; +}; + +} + +#endif diff --git a/louloulibs/utils/sha1.cpp b/louloulibs/utils/sha1.cpp new file mode 100644 index 0000000..76476df --- /dev/null +++ b/louloulibs/utils/sha1.cpp @@ -0,0 +1,154 @@ +/* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + +#include "sha1.hpp" + +#define SHA1_K0 0x5a827999 +#define SHA1_K20 0x6ed9eba1 +#define SHA1_K40 0x8f1bbcdc +#define SHA1_K60 0xca62c1d6 + +const uint8_t sha1InitState[] = { + 0x01,0x23,0x45,0x67, // H0 + 0x89,0xab,0xcd,0xef, // H1 + 0xfe,0xdc,0xba,0x98, // H2 + 0x76,0x54,0x32,0x10, // H3 + 0xf0,0xe1,0xd2,0xc3 // H4 +}; + +void sha1_init(sha1nfo *s) { + memcpy(s->state.b,sha1InitState,HASH_LENGTH); + s->byteCount = 0; + s->bufferOffset = 0; +} + +uint32_t sha1_rol32(uint32_t number, uint8_t bits) { + return ((number << bits) | (number >> (32-bits))); +} + +void sha1_hashBlock(sha1nfo *s) { + uint8_t i; + uint32_t a,b,c,d,e,t; + + a=s->state.w[0]; + b=s->state.w[1]; + c=s->state.w[2]; + d=s->state.w[3]; + e=s->state.w[4]; + for (i=0; i<80; i++) { + if (i>=16) { + t = s->buffer.w[(i+13)&15] ^ s->buffer.w[(i+8)&15] ^ s->buffer.w[(i+2)&15] ^ s->buffer.w[i&15]; + s->buffer.w[i&15] = sha1_rol32(t,1); + } + if (i<20) { + t = (d ^ (b & (c ^ d))) + SHA1_K0; + } else if (i<40) { + t = (b ^ c ^ d) + SHA1_K20; + } else if (i<60) { + t = ((b & c) | (d & (b | c))) + SHA1_K40; + } else { + t = (b ^ c ^ d) + SHA1_K60; + } + t+=sha1_rol32(a,5) + e + s->buffer.w[i&15]; + e=d; + d=c; + c=sha1_rol32(b,30); + b=a; + a=t; + } + s->state.w[0] += a; + s->state.w[1] += b; + s->state.w[2] += c; + s->state.w[3] += d; + s->state.w[4] += e; +} + +void sha1_addUncounted(sha1nfo *s, uint8_t data) { + s->buffer.b[s->bufferOffset ^ 3] = data; + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + sha1_hashBlock(s); + s->bufferOffset = 0; + } +} + +void sha1_writebyte(sha1nfo *s, uint8_t data) { + ++s->byteCount; + sha1_addUncounted(s, data); +} + +void sha1_write(sha1nfo *s, const char *data, size_t len) { + for (;len--;) sha1_writebyte(s, (uint8_t) *data++); +} + +void sha1_pad(sha1nfo *s) { + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); +} + +uint8_t* sha1_result(sha1nfo *s) { + int i; + // Pad to complete the last block + sha1_pad(s); + + // Swap byte order back + for (i=0; i<5; i++) { + uint32_t a,b; + a=s->state.w[i]; + b=a<<24; + b|=(a<<8) & 0x00ff0000; + b|=(a>>8) & 0x0000ff00; + b|=a>>24; + s->state.w[i]=b; + } + + // Return pointer to hash (20 characters) + return s->state.b; +} + +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c + +void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength) { + uint8_t i; + memset(s->keyBuffer, 0, BLOCK_LENGTH); + if (keyLength > BLOCK_LENGTH) { + // Hash long keys + sha1_init(s); + for (;keyLength--;) sha1_writebyte(s, *key++); + memcpy(s->keyBuffer, sha1_result(s), HASH_LENGTH); + } else { + // Block length keys are used as is + memcpy(s->keyBuffer, key, keyLength); + } + // Start inner hash + sha1_init(s); + for (i=0; ikeyBuffer[i] ^ HMAC_IPAD); + } +} + +uint8_t* sha1_resultHmac(sha1nfo *s) { + uint8_t i; + // Complete inner hash + memcpy(s->innerHash,sha1_result(s),HASH_LENGTH); + // Calculate outer hash + sha1_init(s); + for (i=0; ikeyBuffer[i] ^ HMAC_OPAD); + for (i=0; iinnerHash[i]); + return sha1_result(s); +} diff --git a/louloulibs/utils/sha1.hpp b/louloulibs/utils/sha1.hpp new file mode 100644 index 0000000..d02de75 --- /dev/null +++ b/louloulibs/utils/sha1.hpp @@ -0,0 +1,35 @@ +/* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + +#include +#include + +#define HASH_LENGTH 20 +#define BLOCK_LENGTH 64 + +union _buffer { + uint8_t b[BLOCK_LENGTH]; + uint32_t w[BLOCK_LENGTH/4]; +}; + +union _state { + uint8_t b[HASH_LENGTH]; + uint32_t w[HASH_LENGTH/4]; +}; + +typedef struct sha1nfo { + union _buffer buffer; + uint8_t bufferOffset; + union _state state; + uint32_t byteCount; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; +} sha1nfo; + +void sha1_init(sha1nfo *s); +void sha1_writebyte(sha1nfo *s, uint8_t data); +void sha1_write(sha1nfo *s, const char *data, size_t len); +uint8_t* sha1_result(sha1nfo *s); +void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength); +uint8_t* sha1_resultHmac(sha1nfo *s); diff --git a/louloulibs/utils/split.cpp b/louloulibs/utils/split.cpp new file mode 100644 index 0000000..80f8dae --- /dev/null +++ b/louloulibs/utils/split.cpp @@ -0,0 +1,19 @@ +#include +#include + +namespace utils +{ + std::vector split(const std::string& s, const char delim, const bool allow_empty) + { + std::vector ret; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) + { + if (item.empty() && !allow_empty) + continue ; + ret.emplace_back(std::move(item)); + } + return ret; + } +} diff --git a/louloulibs/utils/split.hpp b/louloulibs/utils/split.hpp new file mode 100644 index 0000000..6b487a9 --- /dev/null +++ b/louloulibs/utils/split.hpp @@ -0,0 +1,12 @@ +#ifndef SPLIT_INCLUDED +# define SPLIT_INCLUDED + +#include +#include + +namespace utils +{ + std::vector split(const std::string &s, const char delim, const bool allow_empty=true); +} + +#endif // SPLIT_INCLUDED diff --git a/louloulibs/utils/timed_events.cpp b/louloulibs/utils/timed_events.cpp new file mode 100644 index 0000000..5010a3f --- /dev/null +++ b/louloulibs/utils/timed_events.cpp @@ -0,0 +1,62 @@ +#include + +TimedEvent::TimedEvent(std::chrono::steady_clock::time_point&& time_point, + std::function callback, const std::string& name): + time_point(std::move(time_point)), + callback(callback), + repeat(false), + repeat_delay(0), + name(name) +{ +} + +TimedEvent::TimedEvent(std::chrono::milliseconds&& duration, + std::function callback, const std::string& name): + time_point(std::chrono::steady_clock::now() + duration), + callback(callback), + repeat(true), + repeat_delay(std::move(duration)), + name(name) +{ +} + +TimedEvent::TimedEvent(TimedEvent&& other): + time_point(std::move(other.time_point)), + callback(std::move(other.callback)), + repeat(other.repeat), + repeat_delay(std::move(other.repeat_delay)), + name(std::move(other.name)) +{ +} + +TimedEvent::~TimedEvent() +{ +} + +bool TimedEvent::is_after(const TimedEvent& other) const +{ + return this->is_after(other.time_point); +} + +bool TimedEvent::is_after(const std::chrono::steady_clock::time_point& time_point) const +{ + return this->time_point >= time_point; +} + +std::chrono::milliseconds TimedEvent::get_timeout() const +{ + auto now = std::chrono::steady_clock::now(); + if (now > this->time_point) + return std::chrono::milliseconds(0); + return std::chrono::duration_cast(this->time_point - now); +} + +void TimedEvent::execute() +{ + this->callback(); +} + +const std::string& TimedEvent::get_name() const +{ + return this->name; +} diff --git a/louloulibs/utils/timed_events.hpp b/louloulibs/utils/timed_events.hpp new file mode 100644 index 0000000..4e2800c --- /dev/null +++ b/louloulibs/utils/timed_events.hpp @@ -0,0 +1,132 @@ +#ifndef TIMED_EVENTS_HPP +# define TIMED_EVENTS_HPP + +#include +#include +#include +#include + +using namespace std::literals::chrono_literals; + +namespace utils { +static constexpr std::chrono::milliseconds no_timeout = std::chrono::milliseconds(-1); +} + +class TimedEventsManager; + +/** + * A callback with an associated date. + */ + +class TimedEvent +{ + friend class TimedEventsManager; +public: + /** + * An event the occurs only once, at the given time_point + */ + explicit TimedEvent(std::chrono::steady_clock::time_point&& time_point, + std::function callback, const std::string& name=""); + explicit TimedEvent(std::chrono::milliseconds&& duration, + std::function callback, const std::string& name=""); + + explicit TimedEvent(TimedEvent&&); + ~TimedEvent(); + /** + * Whether or not this event happens after the other one. + */ + bool is_after(const TimedEvent& other) const; + bool is_after(const std::chrono::steady_clock::time_point& time_point) const; + /** + * Return the duration difference between now and the event time point. + * If the difference would be negative (i.e. the event is expired), the + * returned value is 0 instead. The value cannot then be negative. + */ + std::chrono::milliseconds get_timeout() const; + void execute(); + const std::string& get_name() const; + +private: + /** + * The next time point at which the event is executed. + */ + std::chrono::steady_clock::time_point time_point; + /** + * The function to execute. + */ + const std::function callback; + /** + * Whether or not this events repeats itself until it is destroyed. + */ + const bool repeat; + /** + * This value is added to the time_point each time the event is executed, + * if repeat is true. Otherwise it is ignored. + */ + const std::chrono::milliseconds repeat_delay; + /** + * A name that is used to identify that event. If you want to find your + * event (for example if you want to cancel it), the name should be + * unique. + */ + const std::string name; + + TimedEvent(const TimedEvent&) = delete; + TimedEvent& operator=(const TimedEvent&) = delete; + TimedEvent& operator=(TimedEvent&&) = delete; +}; + +/** + * A class managing a list of TimedEvents. + * They are sorted, new events can be added, removed, fetch, etc. + */ + +class TimedEventsManager +{ +public: + ~TimedEventsManager(); + /** + * Return the unique instance of this class + */ + static TimedEventsManager& instance(); + /** + * Add an event to the list of managed events. The list is sorted after + * this call. + */ + void add_event(TimedEvent&& event); + /** + * Returns the duration, in milliseconds, between now and the next + * available event. If the event is already expired (the duration is + * negative), 0 is returned instead (as in “it's not too late, execute it + * now”) + * Returns a negative value if no event is available. + */ + std::chrono::milliseconds get_timeout() const; + /** + * Execute all the expired events (if their expiration time is exactly + * now, or before now). The event is then removed from the list. If the + * event does repeat, its expiration time is updated and it is reinserted + * in the list at the correct position. + * Returns the number of executed events. + */ + std::size_t execute_expired_events(); + /** + * Remove (and thus cancel) all the timed events with the given name. + * Returns the number of canceled events. + */ + std::size_t cancel(const std::string& name); + /** + * Return the number of managed events. + */ + std::size_t size() const; + +private: + explicit TimedEventsManager(); + std::list events; + TimedEventsManager(const TimedEventsManager&) = delete; + TimedEventsManager(TimedEventsManager&&) = delete; + TimedEventsManager& operator=(const TimedEventsManager&) = delete; + TimedEventsManager& operator=(TimedEventsManager&&) = delete; +}; + +#endif // TIMED_EVENTS_HPP diff --git a/louloulibs/utils/timed_events_manager.cpp b/louloulibs/utils/timed_events_manager.cpp new file mode 100644 index 0000000..2c75e48 --- /dev/null +++ b/louloulibs/utils/timed_events_manager.cpp @@ -0,0 +1,81 @@ +#include + +TimedEventsManager& TimedEventsManager::instance() +{ + static TimedEventsManager inst; + return inst; +} + +TimedEventsManager::TimedEventsManager() +{ +} + +TimedEventsManager::~TimedEventsManager() +{ +} + +void TimedEventsManager::add_event(TimedEvent&& event) +{ + for (auto it = this->events.begin(); it != this->events.end(); ++it) + { + if (it->is_after(event)) + { + this->events.emplace(it, std::move(event)); + return; + } + } + this->events.emplace_back(std::move(event)); +} + +std::chrono::milliseconds TimedEventsManager::get_timeout() const +{ + if (this->events.empty()) + return utils::no_timeout; + return this->events.front().get_timeout() + std::chrono::milliseconds(1); +} + +std::size_t TimedEventsManager::execute_expired_events() +{ + std::size_t count = 0; + const auto now = std::chrono::steady_clock::now(); + for (auto it = this->events.begin(); it != this->events.end();) + { + if (!it->is_after(now)) + { + TimedEvent copy(std::move(*it)); + it = this->events.erase(it); + ++count; + copy.execute(); + if (copy.repeat) + { + copy.time_point += copy.repeat_delay; + this->add_event(std::move(copy)); + } + continue; + } + else + break; + } + return count; +} + +std::size_t TimedEventsManager::cancel(const std::string& name) +{ + std::size_t res = 0; + for (auto it = this->events.begin(); it != this->events.end();) + { + if (it->get_name() == name) + { + it = this->events.erase(it); + res++; + } + else + ++it; + } + return res; +} + +std::size_t TimedEventsManager::size() const +{ + return this->events.size(); +} diff --git a/louloulibs/utils/tolower.cpp b/louloulibs/utils/tolower.cpp new file mode 100644 index 0000000..3e518bd --- /dev/null +++ b/louloulibs/utils/tolower.cpp @@ -0,0 +1,13 @@ +#include + +namespace utils +{ + std::string tolower(const std::string& original) + { + std::string res; + res.reserve(original.size()); + for (const char c: original) + res += static_cast(std::tolower(c)); + return res; + } +} diff --git a/louloulibs/utils/tolower.hpp b/louloulibs/utils/tolower.hpp new file mode 100644 index 0000000..0019182 --- /dev/null +++ b/louloulibs/utils/tolower.hpp @@ -0,0 +1,11 @@ +#ifndef TOLOWER_INCLUDED +# define TOLOWER_INCLUDED + +#include + +namespace utils +{ + std::string tolower(const std::string& original); +} + +#endif // SPLIT_INCLUDED diff --git a/louloulibs/xmpp/adhoc_command.cpp b/louloulibs/xmpp/adhoc_command.cpp new file mode 100644 index 0000000..24145f5 --- /dev/null +++ b/louloulibs/xmpp/adhoc_command.cpp @@ -0,0 +1,105 @@ +#include +#include +#include + +using namespace std::string_literals; + +AdhocCommand::AdhocCommand(std::vector&& callbacks, const std::string& name, const bool admin_only): + name(name), + callbacks(std::move(callbacks)), + admin_only(admin_only) +{ +} + +AdhocCommand::~AdhocCommand() +{ +} + +bool AdhocCommand::is_admin_only() const +{ + return this->admin_only; +} + +void PingStep1(XmppComponent*, AdhocSession&, XmlNode& command_node) +{ + XmlNode note("note"); + note["type"] = "info"; + note.set_inner("Pong"); + note.close(); + command_node.add_child(std::move(note)); +} + +void HelloStep1(XmppComponent*, AdhocSession&, XmlNode& command_node) +{ + XmlNode x("jabber:x:data:x"); + x["type"] = "form"; + XmlNode title("title"); + title.set_inner("Configure your name."); + title.close(); + x.add_child(std::move(title)); + XmlNode instructions("instructions"); + instructions.set_inner("Please provide your name."); + instructions.close(); + x.add_child(std::move(instructions)); + XmlNode name_field("field"); + name_field["var"] = "name"; + name_field["type"] = "text-single"; + name_field["label"] = "Your name"; + XmlNode required("required"); + required.close(); + name_field.add_child(std::move(required)); + name_field.close(); + x.add_child(std::move(name_field)); + x.close(); + command_node.add_child(std::move(x)); +} + +void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node) +{ + // Find out if the name was provided in the form. + XmlNode* x = command_node.get_child("x", "jabber:x:data"); + if (x) + { + XmlNode* name_field = nullptr; + for (XmlNode* field: x->get_children("field", "jabber:x:data")) + if (field->get_tag("var") == "name") + { + name_field = field; + break; + } + if (name_field) + { + XmlNode* value = name_field->get_child("value", "jabber:x:data"); + if (value) + { + XmlNode note("note"); + note["type"] = "info"; + note.set_inner("Hello "s + value->get_inner() + "!"s); + note.close(); + command_node.delete_all_children(); + command_node.add_child(std::move(note)); + return; + } + } + } + command_node.delete_all_children(); + XmlNode error(ADHOC_NS":error"); + error["type"] = "modify"; + XmlNode condition(STANZA_NS":bad-request"); + condition.close(); + error.add_child(std::move(condition)); + error.close(); + command_node.add_child(std::move(error)); + session.terminate(); +} + +void Reload(XmppComponent*, AdhocSession&, XmlNode& command_node) +{ + ::reload_process(); + command_node.delete_all_children(); + XmlNode note("note"); + note["type"] = "info"; + note.set_inner("Configuration reloaded."); + note.close(); + command_node.add_child(std::move(note)); +} diff --git a/louloulibs/xmpp/adhoc_command.hpp b/louloulibs/xmpp/adhoc_command.hpp new file mode 100644 index 0000000..1ff2bcf --- /dev/null +++ b/louloulibs/xmpp/adhoc_command.hpp @@ -0,0 +1,43 @@ +#ifndef ADHOC_COMMAND_HPP +# define ADHOC_COMMAND_HPP + +/** + * Describe an ad-hoc command. + * + * Can only have zero or one step for now. When execution is requested, it + * can return a result immediately, or provide a form to be filled, and + * provide a result once the filled form is received. + */ + +#include + +#include +#include + +class AdhocCommand +{ + friend class AdhocSession; +public: + AdhocCommand(std::vector&& callback, const std::string& name, const bool admin_only); + ~AdhocCommand(); + + const std::string name; + + bool is_admin_only() const; + +private: + /** + * A command may have one or more steps. Each step is a different + * callback, inserting things into a XmlNode and calling + * methods of an AdhocSession. + */ + std::vector callbacks; + const bool admin_only; +}; + +void PingStep1(XmppComponent*, AdhocSession& session, XmlNode& command_node); +void HelloStep1(XmppComponent*, AdhocSession& session, XmlNode& command_node); +void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node); +void Reload(XmppComponent*, AdhocSession& session, XmlNode& command_node); + +#endif // ADHOC_COMMAND_HPP diff --git a/louloulibs/xmpp/adhoc_commands_handler.cpp b/louloulibs/xmpp/adhoc_commands_handler.cpp new file mode 100644 index 0000000..46c8a32 --- /dev/null +++ b/louloulibs/xmpp/adhoc_commands_handler.cpp @@ -0,0 +1,133 @@ +#include +#include + +#include +#include +#include +#include + +#include + +using namespace std::string_literals; + +const std::map& AdhocCommandsHandler::get_commands() const +{ + return this->commands; +} + +std::map& AdhocCommandsHandler::get_commands() +{ + return this->commands; +} + +XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, XmlNode command_node) +{ + std::string action = command_node.get_tag("action"); + if (action.empty()) + action = "execute"; + command_node.del_tag("action"); + + Jid jid(executor_jid); + + const std::string node = command_node.get_tag("node"); + auto command_it = this->commands.find(node); + if (command_it == this->commands.end()) + { + XmlNode error(ADHOC_NS":error"); + error["type"] = "cancel"; + XmlNode condition(STANZA_NS":item-not-found"); + condition.close(); + error.add_child(std::move(condition)); + error.close(); + command_node.add_child(std::move(error)); + } + else if (command_it->second.is_admin_only() && + Config::get("admin", "") != jid.local + "@" + jid.domain) + { + XmlNode error(ADHOC_NS":error"); + error["type"] = "cancel"; + XmlNode condition(STANZA_NS":forbidden"); + condition.close(); + error.add_child(std::move(condition)); + error.close(); + command_node.add_child(std::move(error)); + } + else + { + std::string sessionid = command_node.get_tag("sessionid"); + if (sessionid.empty()) + { // create a new session, with a new id + sessionid = XmppComponent::next_id(); + command_node["sessionid"] = sessionid; + this->sessions.emplace(std::piecewise_construct, + std::forward_as_tuple(sessionid, executor_jid), + std::forward_as_tuple(command_it->second, executor_jid)); + TimedEventsManager::instance().add_event(TimedEvent(std::chrono::steady_clock::now() + 3600s, + std::bind(&AdhocCommandsHandler::remove_session, this, sessionid, executor_jid), + "adhocsession"s + sessionid + executor_jid)); + } + auto session_it = this->sessions.find(std::make_pair(sessionid, executor_jid)); + if (session_it == this->sessions.end()) + { + XmlNode error(ADHOC_NS":error"); + error["type"] = "modify"; + XmlNode condition(STANZA_NS":bad-request"); + condition.close(); + error.add_child(std::move(condition)); + error.close(); + command_node.add_child(std::move(error)); + } + else if (action == "execute" || action == "next" || action == "complete") + { + // execute the step + AdhocSession& session = session_it->second; + const AdhocStep& step = session.get_next_step(); + step(this->xmpp_component, session, command_node); + if (session.remaining_steps() == 0 || + session.is_terminated()) + { + this->sessions.erase(session_it); + command_node["status"] = "completed"; + TimedEventsManager::instance().cancel("adhocsession"s + sessionid + executor_jid); + } + else + { + command_node["status"] = "executing"; + XmlNode actions("actions"); + XmlNode next("next"); + next.close(); + actions.add_child(std::move(next)); + actions.close(); + command_node.add_child(std::move(actions)); + } + } + else if (action == "cancel") + { + this->sessions.erase(session_it); + command_node["status"] = "canceled"; + TimedEventsManager::instance().cancel("adhocsession"s + sessionid + executor_jid); + } + else // unsupported action + { + XmlNode error(ADHOC_NS":error"); + error["type"] = "modify"; + XmlNode condition(STANZA_NS":bad-request"); + condition.close(); + error.add_child(std::move(condition)); + error.close(); + command_node.add_child(std::move(error)); + } + } + return command_node; +} + +void AdhocCommandsHandler::remove_session(const std::string& session_id, const std::string& initiator_jid) +{ + auto session_it = this->sessions.find(std::make_pair(session_id, initiator_jid)); + if (session_it != this->sessions.end()) + { + this->sessions.erase(session_it); + return ; + } + log_error("Tried to remove ad-hoc session for [" << session_id << ", " << initiator_jid << "] but none found"); +} diff --git a/louloulibs/xmpp/adhoc_commands_handler.hpp b/louloulibs/xmpp/adhoc_commands_handler.hpp new file mode 100644 index 0000000..1083c44 --- /dev/null +++ b/louloulibs/xmpp/adhoc_commands_handler.hpp @@ -0,0 +1,74 @@ +#ifndef ADHOC_COMMANDS_HANDLER_HPP +# define ADHOC_COMMANDS_HANDLER_HPP + +/** + * Manage a list of available AdhocCommands and the list of ongoing + * AdhocCommandSessions. + */ + +#include +#include + +#include +#include +#include + +class AdhocCommandsHandler +{ +public: + explicit AdhocCommandsHandler(XmppComponent* xmpp_component): + xmpp_component(xmpp_component), + commands{} + { } + ~AdhocCommandsHandler() = default; + /** + * Returns the list of available commands. + */ + const std::map& get_commands() const; + /** + * This one can be used to add new commands. + */ + std::map& get_commands(); + /** + * Find the requested command, create a new session or use an existing + * one, and process the request (provide a new form, an error, or a + * result). + * + * Returns a (moved) XmlNode that will be inserted in the iq response. It + * should be a node containing one or more useful children. If + * it contains an node, the iq response will have an error type. + * + * Takes a copy of the node so we can actually edit it and use + * it as our return value. + */ + XmlNode handle_request(const std::string& executor_jid, XmlNode command_node); + /** + * Remove the session from the list. This is done to avoid filling the + * memory with waiting session (for example due to a client that starts + * multi-steps command but never finishes them). + */ + void remove_session(const std::string& session_id, const std::string& initiator_jid); +private: + /** + * A pointer to the XmppComponent, to access to basically anything in the + * gateway. + */ + XmppComponent* xmpp_component; + /** + * The list of all available commands. + */ + std::map commands; + /** + * The list of all currently on-going commands. + * + * Of the form: {{session_id, owner_jid}, session}. + */ + std::map, AdhocSession> sessions; + + AdhocCommandsHandler(const AdhocCommandsHandler&) = delete; + AdhocCommandsHandler(AdhocCommandsHandler&&) = delete; + AdhocCommandsHandler& operator=(const AdhocCommandsHandler&) = delete; + AdhocCommandsHandler& operator=(AdhocCommandsHandler&&) = delete; +}; + +#endif // ADHOC_COMMANDS_HANDLER_HPP diff --git a/louloulibs/xmpp/adhoc_session.cpp b/louloulibs/xmpp/adhoc_session.cpp new file mode 100644 index 0000000..fc60bb7 --- /dev/null +++ b/louloulibs/xmpp/adhoc_session.cpp @@ -0,0 +1,37 @@ +#include +#include + +#include + +AdhocSession::AdhocSession(const AdhocCommand& command, const std::string& jid): + command(command), + owner_jid(jid), + current_step(0), + terminated(false) +{ +} + +AdhocSession::~AdhocSession() +{ +} + +const AdhocStep& AdhocSession::get_next_step() +{ + assert(this->current_step < this->command.callbacks.size()); + return this->command.callbacks[this->current_step++]; +} + +size_t AdhocSession::remaining_steps() const +{ + return this->command.callbacks.size() - this->current_step; +} + +bool AdhocSession::is_terminated() const +{ + return this->terminated; +} + +void AdhocSession::terminate() +{ + this->terminated = true; +} diff --git a/louloulibs/xmpp/adhoc_session.hpp b/louloulibs/xmpp/adhoc_session.hpp new file mode 100644 index 0000000..ddfb2fe --- /dev/null +++ b/louloulibs/xmpp/adhoc_session.hpp @@ -0,0 +1,70 @@ +#ifndef ADHOC_SESSION_HPP +# define ADHOC_SESSION_HPP + +#include + +#include +#include + +class XmppComponent; + +class AdhocCommand; +class AdhocSession; + +/** + * A function executed as an ad-hoc command step. It takes a + * XmlNode and modifies it accordingly (inserting for example an + * node, or a data form…). + * TODO fix this: + * It also must call one of step_passed(), cancel() etc on the AdhocSession object. + */ +typedef std::function AdhocStep; + +class AdhocSession +{ +public: + explicit AdhocSession(const AdhocCommand& command, const std::string& jid); + ~AdhocSession(); + /** + * Return the function to be executed, found in our AdhocCommand, for the + * current_step. And increment the current_step. + */ + const AdhocStep& get_next_step(); + /** + * Return the number of remaining steps. + */ + size_t remaining_steps() const; + /** + * This may be modified by an AdhocStep, to indicate that this session + * should no longer exist, because we encountered an error, and we can't + * execute any more step of it. + */ + void terminate(); + bool is_terminated() const; + +private: + /** + * A reference of the command concerned by this session. Used for example + * to get the next step of that command, things like that. + */ + const AdhocCommand& command; + /** + * The full JID of the XMPP user that created this session by executing + * the first step of a command. Only that JID must be allowed to access + * this session. + */ + const std::string& owner_jid; + /** + * The current step we are at. It starts at zero. It is used to index the + * associated AdhocCommand::callbacks vector. + */ + size_t current_step; + bool terminated; + + AdhocSession(const AdhocSession&) = delete; + AdhocSession(AdhocSession&&) = delete; + AdhocSession& operator=(const AdhocSession&) = delete; + AdhocSession& operator=(AdhocSession&&) = delete; +}; + +#endif // ADHOC_SESSION_HPP diff --git a/louloulibs/xmpp/body.hpp b/louloulibs/xmpp/body.hpp new file mode 100644 index 0000000..6ac678e --- /dev/null +++ b/louloulibs/xmpp/body.hpp @@ -0,0 +1,12 @@ +#ifndef XMPP_BODY_HPP_INCLUDED +#define XMPP_BODY_HPP_INCLUDED + +namespace Xmpp +{ +// Contains: +// - an XMPP-valid UTF-8 body +// - an XML node representing the XHTML-IM body, or null + typedef std::tuple> body; +} + +#endif /* XMPP_BODY_HPP_INCLUDED */ diff --git a/louloulibs/xmpp/jid.cpp b/louloulibs/xmpp/jid.cpp new file mode 100644 index 0000000..e6fee45 --- /dev/null +++ b/louloulibs/xmpp/jid.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include + +#include +#ifdef LIBIDN_FOUND + #include +#endif + +#include + +Jid::Jid(const std::string& jid) +{ + std::string::size_type slash = jid.find('/'); + if (slash != std::string::npos) + { + this->resource = jid.substr(slash + 1); + } + + std::string::size_type at = jid.find('@'); + if (at != std::string::npos && at < slash) + { + this->local = jid.substr(0, at); + at++; + } + else + at = 0; + + this->domain = jid.substr(at, slash - at); +} + +#include + +static constexpr size_t max_jid_part_len = 1023; + +std::string jidprep(const std::string& original) +{ +#ifdef LIBIDN_FOUND + using CacheType = std::map; + static CacheType cache; + std::pair cached = cache.insert({original, {}}); + if (std::get<1>(cached) == false) + { // Insertion failed: the result is already in the cache, return it + return std::get<0>(cached)->second; + } + + const std::string error_msg("Failed to convert " + original + " into a valid JID:"); + Jid jid(original); + + char local[max_jid_part_len] = {}; + memcpy(local, jid.local.data(), jid.local.size()); + Stringprep_rc rc = static_cast(::stringprep(local, max_jid_part_len, + static_cast(0), stringprep_xmpp_nodeprep)); + if (rc != STRINGPREP_OK) + { + log_error(error_msg + stringprep_strerror(rc)); + return ""; + } + + char domain[max_jid_part_len] = {}; + memcpy(domain, jid.domain.data(), jid.domain.size()); + rc = static_cast(::stringprep(domain, max_jid_part_len, + static_cast(0), stringprep_nameprep)); + if (rc != STRINGPREP_OK) + { + log_error(error_msg + stringprep_strerror(rc)); + return ""; + } + std::replace_if(std::begin(domain), domain + ::strlen(domain), + [](const char c) -> bool + { + return !((c >= 'a' && c <= 'z') || c == '-' || + (c >= '0' && c <= '9') || c == '.'); + }, '-'); + + // If there is no resource, stop here + if (jid.resource.empty()) + { + std::get<0>(cached)->second = std::string(local) + "@" + domain; + return std::get<0>(cached)->second; + } + + // Otherwise, also process the resource part + char resource[max_jid_part_len] = {}; + memcpy(resource, jid.resource.data(), jid.resource.size()); + rc = static_cast(::stringprep(resource, max_jid_part_len, + static_cast(0), stringprep_xmpp_resourceprep)); + if (rc != STRINGPREP_OK) + { + log_error(error_msg + stringprep_strerror(rc)); + return ""; + } + std::get<0>(cached)->second = std::string(local) + "@" + domain + "/" + resource; + return std::get<0>(cached)->second; + +#else + (void)original; + return ""; +#endif +} diff --git a/louloulibs/xmpp/jid.hpp b/louloulibs/xmpp/jid.hpp new file mode 100644 index 0000000..b6975a2 --- /dev/null +++ b/louloulibs/xmpp/jid.hpp @@ -0,0 +1,36 @@ +#ifndef JID_INCLUDED +# define JID_INCLUDED + +#include + +/** + * Parse a JID into its different subart + */ +class Jid +{ +public: + explicit Jid(const std::string& jid); + + std::string domain; + std::string local; + std::string resource; + +private: + Jid(const Jid&) = delete; + Jid(Jid&&) = delete; + Jid& operator=(const Jid&) = delete; + Jid& operator=(Jid&&) = delete; +}; + +/** + * Prepare the given UTF-8 string according to the XMPP node stringprep + * identifier profile. This is used to send properly-formed JID to the XMPP + * server. + * + * If the stringprep library is not found, we return an empty string. When + * this function is used, the result must always be checked for an empty + * value, and if this is the case it must not be used as a JID. + */ +std::string jidprep(const std::string& original); + +#endif // JID_INCLUDED diff --git a/louloulibs/xmpp/roster.cpp b/louloulibs/xmpp/roster.cpp new file mode 100644 index 0000000..a14a384 --- /dev/null +++ b/louloulibs/xmpp/roster.cpp @@ -0,0 +1,21 @@ +#include + +RosterItem::RosterItem(const std::string& jid, const std::string& name, + std::vector& groups): + jid(jid), + name(name), + groups(groups) +{ +} + +RosterItem::RosterItem(const std::string& jid, const std::string& name): + jid(jid), + name(name), + groups{} +{ +} + +void Roster::clear() +{ + this->items.clear(); +} diff --git a/louloulibs/xmpp/roster.hpp b/louloulibs/xmpp/roster.hpp new file mode 100644 index 0000000..0aebca5 --- /dev/null +++ b/louloulibs/xmpp/roster.hpp @@ -0,0 +1,71 @@ +#ifndef ROSTER_HPP_INCLUDED +#define ROSTER_HPP_INCLUDED + +#include +#include +#include + +class RosterItem +{ +public: + RosterItem(const std::string& jid, const std::string& name, + std::vector& groups); + RosterItem(const std::string& jid, const std::string& name); + RosterItem() = default; + ~RosterItem() = default; + RosterItem(const RosterItem&) = default; + RosterItem(RosterItem&&) = default; + RosterItem& operator=(const RosterItem&) = default; + RosterItem& operator=(RosterItem&&) = default; + + std::string jid; + std::string name; + std::vector groups; + +private: +}; + +/** + * Keep track of the last known stat of a JID's roster + */ +class Roster +{ +public: + Roster() = default; + ~Roster() = default; + + void clear(); + + template + RosterItem* add_item(ArgsType&&... args) + { + this->items.emplace_back(std::forward(args)...); + auto it = this->items.end() - 1; + return &*it; + } + RosterItem* get_item(const std::string& jid) + { + auto it = std::find_if(this->items.begin(), this->items.end(), + [this, &jid](const auto& item) + { + return item.jid == jid; + }); + if (it != this->items.end()) + return &*it; + return nullptr; + } + const std::vector& get_items() const + { + return this->items; + } + +private: + std::vector items; + + Roster(const Roster&) = delete; + Roster(Roster&&) = delete; + Roster& operator=(const Roster&) = delete; + Roster& operator=(Roster&&) = delete; +}; + +#endif /* ROSTER_HPP_INCLUDED */ diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp new file mode 100644 index 0000000..1048f86 --- /dev/null +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -0,0 +1,716 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#ifdef SYSTEMD_FOUND +# include +#endif + +using namespace std::string_literals; + +static std::set kickable_errors{ + "gone", + "internal-server-error", + "item-not-found", + "jid-malformed", + "recipient-unavailable", + "redirect", + "remote-server-not-found", + "remote-server-timeout", + "service-unavailable", + "malformed-error" + }; + +XmppComponent::XmppComponent(std::shared_ptr poller, const std::string& hostname, const std::string& secret): + TCPSocketHandler(poller), + ever_auth(false), + first_connection_try(true), + secret(secret), + authenticated(false), + doc_open(false), + served_hostname(hostname), + stanza_handlers{}, + adhoc_commands_handler(this) +{ + this->parser.add_stream_open_callback(std::bind(&XmppComponent::on_remote_stream_open, this, + std::placeholders::_1)); + this->parser.add_stanza_callback(std::bind(&XmppComponent::on_stanza, this, + std::placeholders::_1)); + this->parser.add_stream_close_callback(std::bind(&XmppComponent::on_remote_stream_close, this, + std::placeholders::_1)); + this->stanza_handlers.emplace("handshake", + std::bind(&XmppComponent::handle_handshake, this,std::placeholders::_1)); + this->stanza_handlers.emplace("error", + std::bind(&XmppComponent::handle_error, this,std::placeholders::_1)); +} + +void XmppComponent::start() +{ + this->connect("127.0.0.1", Config::get("port", "5347"), false); +} + +bool XmppComponent::is_document_open() const +{ + return this->doc_open; +} + +void XmppComponent::send_stanza(const Stanza& stanza) +{ + std::string str = stanza.to_string(); + log_debug("XMPP SENDING: " << str); + this->send_data(std::move(str)); +} + +void XmppComponent::on_connection_failed(const std::string& reason) +{ + this->first_connection_try = false; + log_error("Failed to connect to the XMPP server: " << reason); +#ifdef SYSTEMD_FOUND + sd_notifyf(0, "STATUS=Failed to connect to the XMPP server: %s", reason.data()); +#endif +} + +void XmppComponent::on_connected() +{ + log_info("connected to XMPP server"); + this->first_connection_try = true; + XmlNode node("", nullptr); + node.set_name("stream:stream"); + node["xmlns"] = COMPONENT_NS; + node["xmlns:stream"] = STREAM_NS; + node["to"] = this->served_hostname; + this->send_stanza(node); + this->doc_open = true; + // We may have some pending data to send: this happens when we try to send + // some data before we are actually connected. We send that data right now, if any + this->send_pending_data(); +} + +void XmppComponent::on_connection_close(const std::string& error) +{ + if (error.empty()) + { + log_info("XMPP server closed connection"); + } + else + { + log_info("XMPP server closed connection: " << error); + } +} + +void XmppComponent::parse_in_buffer(const size_t size) +{ + if (!this->in_buf.empty()) + { // This may happen if the parser could not allocate enough space for + // us. We try to feed it the data that was read into our in_buf + // instead. If this fails again we are in trouble. + this->parser.feed(this->in_buf.data(), this->in_buf.size(), false); + this->in_buf.clear(); + } + else + { // Just tell the parser to parse the data that was placed into the + // buffer it provided to us with GetBuffer + this->parser.parse(size, false); + } +} + +void XmppComponent::on_remote_stream_open(const XmlNode& node) +{ + log_debug("XMPP DOCUMENT OPEN: " << node.to_string()); + this->stream_id = node.get_tag("id"); + if (this->stream_id.empty()) + { + log_error("Error: no attribute 'id' found"); + this->send_stream_error("bad-format", "missing 'id' attribute"); + this->close_document(); + return ; + } + + // Try to authenticate + char digest[HASH_LENGTH * 2 + 1]; + sha1nfo sha1; + sha1_init(&sha1); + sha1_write(&sha1, this->stream_id.data(), this->stream_id.size()); + sha1_write(&sha1, this->secret.data(), this->secret.size()); + const uint8_t* result = sha1_result(&sha1); + for (int i=0; i < HASH_LENGTH; i++) + sprintf(digest + (i*2), "%02x", result[i]); + digest[HASH_LENGTH * 2] = '\0'; + + Stanza handshake(COMPONENT_NS":handshake"); + handshake.set_inner(digest); + handshake.close(); + this->send_stanza(handshake); +} + +void XmppComponent::on_remote_stream_close(const XmlNode& node) +{ + log_debug("XMPP DOCUMENT CLOSE " << node.to_string()); + this->doc_open = false; +} + +void XmppComponent::reset() +{ + this->parser.reset(); +} + +void XmppComponent::on_stanza(const Stanza& stanza) +{ + log_debug("XMPP RECEIVING: " << stanza.to_string()); + std::function handler; + try + { + handler = this->stanza_handlers.at(stanza.get_name()); + } + catch (const std::out_of_range& exception) + { + log_warning("No handler for stanza of type " << stanza.get_name()); + return; + } + handler(stanza); +} + +void XmppComponent::send_stream_error(const std::string& name, const std::string& explanation) +{ + XmlNode node("stream:error", nullptr); + XmlNode error(name, nullptr); + error["xmlns"] = STREAM_NS; + if (!explanation.empty()) + error.set_inner(explanation); + error.close(); + node.add_child(std::move(error)); + node.close(); + this->send_stanza(node); +} + +void XmppComponent::send_stanza_error(const std::string& kind, const std::string& to, const std::string& from, + const std::string& id, const std::string& error_type, + const std::string& defined_condition, const std::string& text, + const bool fulljid) +{ + Stanza node(kind); + if (!to.empty()) + node["to"] = to; + if (!from.empty()) + { + if (fulljid) + node["from"] = from; + else + node["from"] = from + "@" + this->served_hostname; + } + if (!id.empty()) + node["id"] = id; + node["type"] = "error"; + XmlNode error("error"); + error["type"] = error_type; + XmlNode inner_error(defined_condition); + inner_error["xmlns"] = STANZA_NS; + inner_error.close(); + error.add_child(std::move(inner_error)); + if (!text.empty()) + { + XmlNode text_node("text"); + text_node["xmlns"] = STANZA_NS; + text_node.set_inner(text); + text_node.close(); + error.add_child(std::move(text_node)); + } + error.close(); + node.add_child(std::move(error)); + node.close(); + this->send_stanza(node); +} + +void XmppComponent::close_document() +{ + log_debug("XMPP SENDING: "); + this->send_data(""); + this->doc_open = false; +} + +void XmppComponent::handle_handshake(const Stanza& stanza) +{ + (void)stanza; + this->authenticated = true; + this->ever_auth = true; + log_info("Authenticated with the XMPP server"); +#ifdef SYSTEMD_FOUND + sd_notify(0, "READY=1"); + // Install an event that sends a keepalive to systemd. If biboumi crashes + // or hangs for too long, systemd will restart it. + uint64_t usec; + if (sd_watchdog_enabled(0, &usec) > 0) + { + TimedEventsManager::instance().add_event(TimedEvent( + std::chrono::duration_cast(std::chrono::microseconds(usec / 2)), + []() { sd_notify(0, "WATCHDOG=1"); })); + } +#endif + this->after_handshake(); +} + +void XmppComponent::handle_error(const Stanza& stanza) +{ + XmlNode* text = stanza.get_child("text", STREAMS_NS); + std::string error_message("Unspecified error"); + if (text) + error_message = text->get_inner(); + log_error("Stream error received from the XMPP server: " << error_message); +#ifdef SYSTEMD_FOUND + if (!this->ever_auth) + sd_notifyf(0, "STATUS=Failed to authenticate to the XMPP server: %s", error_message.data()); +#endif + +} + +void* XmppComponent::get_receive_buffer(const size_t size) const +{ + return this->parser.get_buffer(size); +} + +void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, const std::string& to, const std::string& type, const bool fulljid) +{ + XmlNode node("message"); + node["to"] = to; + if (fulljid) + node["from"] = from; + else + node["from"] = from + "@" + this->served_hostname; + if (!type.empty()) + node["type"] = type; + XmlNode body_node("body"); + body_node.set_inner(std::get<0>(body)); + body_node.close(); + node.add_child(std::move(body_node)); + if (std::get<1>(body)) + { + XmlNode html("html"); + html["xmlns"] = XHTMLIM_NS; + // Pass the ownership of the pointer to this xmlnode + html.add_child(std::get<1>(body).release()); + html.close(); + node.add_child(std::move(html)); + } + node.close(); + this->send_stanza(node); +} + +void XmppComponent::send_user_join(const std::string& from, + const std::string& nick, + const std::string& realjid, + const std::string& affiliation, + const std::string& role, + const std::string& to, + const bool self) +{ + XmlNode node("presence"); + node["to"] = to; + node["from"] = from + "@" + this->served_hostname + "/" + nick; + + XmlNode x("x"); + x["xmlns"] = MUC_USER_NS; + + XmlNode item("item"); + if (!affiliation.empty()) + item["affiliation"] = affiliation; + if (!role.empty()) + item["role"] = role; + if (!realjid.empty()) + { + const std::string preped_jid = jidprep(realjid); + if (!preped_jid.empty()) + item["jid"] = preped_jid; + } + item.close(); + x.add_child(std::move(item)); + + if (self) + { + XmlNode status("status"); + status["code"] = "110"; + status.close(); + x.add_child(std::move(status)); + } + x.close(); + node.add_child(std::move(x)); + node.close(); + this->send_stanza(node); +} + +void XmppComponent::send_invalid_room_error(const std::string& muc_name, + const std::string& nick, + const std::string& to) +{ + Stanza presence("presence"); + if (!muc_name.empty()) + presence["from"] = muc_name + "@" + this->served_hostname + "/" + nick; + else + presence["from"] = this->served_hostname; + presence["to"] = to; + presence["type"] = "error"; + XmlNode x("x"); + x["xmlns"] = MUC_NS; + x.close(); + presence.add_child(std::move(x)); + XmlNode error("error"); + error["by"] = muc_name + "@" + this->served_hostname; + error["type"] = "cancel"; + XmlNode item_not_found("item-not-found"); + item_not_found["xmlns"] = STANZA_NS; + item_not_found.close(); + error.add_child(std::move(item_not_found)); + XmlNode text("text"); + text["xmlns"] = STANZA_NS; + text["xml:lang"] = "en"; + text.set_inner(muc_name + + " is not a valid IRC channel name. A correct room jid is of the form: #%@" + + this->served_hostname); + text.close(); + error.add_child(std::move(text)); + error.close(); + presence.add_child(std::move(error)); + presence.close(); + this->send_stanza(presence); +} + +void XmppComponent::send_invalid_user_error(const std::string& user_name, const std::string& to) +{ + Stanza message("message"); + message["from"] = user_name + "@" + this->served_hostname; + message["to"] = to; + message["type"] = "error"; + XmlNode x("x"); + x["xmlns"] = MUC_NS; + x.close(); + message.add_child(std::move(x)); + XmlNode error("error"); + error["type"] = "cancel"; + XmlNode item_not_found("item-not-found"); + item_not_found["xmlns"] = STANZA_NS; + item_not_found.close(); + error.add_child(std::move(item_not_found)); + XmlNode text("text"); + text["xmlns"] = STANZA_NS; + text["xml:lang"] = "en"; + text.set_inner(user_name + + " is not a valid IRC user name. A correct user jid is of the form: !@" + + this->served_hostname); + text.close(); + error.add_child(std::move(text)); + error.close(); + message.add_child(std::move(error)); + message.close(); + this->send_stanza(message); +} + +void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, const std::string& to) +{ + XmlNode message("message"); + message["to"] = to; + message["from"] = from + "@" + this->served_hostname; + message["type"] = "groupchat"; + XmlNode subject("subject"); + subject.set_inner(std::get<0>(topic)); + subject.close(); + message.add_child(std::move(subject)); + message.close(); + this->send_stanza(message); +} + +void XmppComponent::send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to) +{ + Stanza message("message"); + message["to"] = jid_to; + if (!nick.empty()) + message["from"] = muc_name + "@" + this->served_hostname + "/" + nick; + else // Message from the room itself + message["from"] = muc_name + "@" + this->served_hostname; + message["type"] = "groupchat"; + XmlNode body("body"); + body.set_inner(std::get<0>(xmpp_body)); + body.close(); + message.add_child(std::move(body)); + if (std::get<1>(xmpp_body)) + { + XmlNode html("html"); + html["xmlns"] = XHTMLIM_NS; + // Pass the ownership of the pointer to this xmlnode + html.add_child(std::get<1>(xmpp_body).release()); + html.close(); + message.add_child(std::move(html)); + } + message.close(); + this->send_stanza(message); +} + +void XmppComponent::send_muc_leave(const std::string& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self) +{ + Stanza presence("presence"); + presence["to"] = jid_to; + presence["from"] = muc_name + "@" + this->served_hostname + "/" + nick; + presence["type"] = "unavailable"; + const std::string message_str = std::get<0>(message); + XmlNode x("x"); + x["xmlns"] = MUC_USER_NS; + if (self) + { + XmlNode status("status"); + status["code"] = "110"; + status.close(); + x.add_child(std::move(status)); + } + x.close(); + presence.add_child(std::move(x)); + if (!message_str.empty()) + { + XmlNode status("status"); + status.set_inner(message_str); + status.close(); + presence.add_child(std::move(status)); + } + presence.close(); + this->send_stanza(presence); +} + +void XmppComponent::send_nick_change(const std::string& muc_name, + const std::string& old_nick, + const std::string& new_nick, + const std::string& affiliation, + const std::string& role, + const std::string& jid_to, + const bool self) +{ + Stanza presence("presence"); + presence["to"] = jid_to; + presence["from"] = muc_name + "@" + this->served_hostname + "/" + old_nick; + presence["type"] = "unavailable"; + XmlNode x("x"); + x["xmlns"] = MUC_USER_NS; + XmlNode item("item"); + item["nick"] = new_nick; + item.close(); + x.add_child(std::move(item)); + XmlNode status("status"); + status["code"] = "303"; + status.close(); + x.add_child(std::move(status)); + if (self) + { + XmlNode status2("status"); + status2["code"] = "110"; + status2.close(); + x.add_child(std::move(status2)); + } + x.close(); + presence.add_child(std::move(x)); + presence.close(); + this->send_stanza(presence); + + this->send_user_join(muc_name, new_nick, "", affiliation, role, jid_to, self); +} + +void XmppComponent::kick_user(const std::string& muc_name, + const std::string& target, + const std::string& txt, + const std::string& author, + const std::string& jid_to) +{ + Stanza presence("presence"); + presence["from"] = muc_name + "@" + this->served_hostname + "/" + target; + presence["to"] = jid_to; + presence["type"] = "unavailable"; + XmlNode x("x"); + x["xmlns"] = MUC_USER_NS; + XmlNode item("item"); + item["affiliation"] = "none"; + item["role"] = "none"; + XmlNode actor("actor"); + actor["nick"] = author; + actor["jid"] = author; // backward compatibility with old clients + actor.close(); + item.add_child(std::move(actor)); + XmlNode reason("reason"); + reason.set_inner(txt); + reason.close(); + item.add_child(std::move(reason)); + item.close(); + x.add_child(std::move(item)); + XmlNode status("status"); + status["code"] = "307"; + status.close(); + x.add_child(std::move(status)); + x.close(); + presence.add_child(std::move(x)); + presence.close(); + this->send_stanza(presence); +} + +void XmppComponent::send_presence_error(const std::string& muc_name, + const std::string& nickname, + const std::string& jid_to, + const std::string& type, + const std::string& condition, + const std::string& error_code, + const std::string& /* text */) +{ + Stanza presence("presence"); + presence["from"] = muc_name + "@" + this->served_hostname + "/" + nickname; + presence["to"] = jid_to; + presence["type"] = "error"; + XmlNode x("x"); + x["xmlns"] = MUC_NS; + x.close(); + presence.add_child(std::move(x)); + XmlNode error("error"); + error["by"] = muc_name + "@" + this->served_hostname; + error["type"] = type; + if (!error_code.empty()) + error["code"] = error_code; + XmlNode subnode(condition); + subnode["xmlns"] = STANZA_NS; + subnode.close(); + error.add_child(std::move(subnode)); + error.close(); + presence.add_child(std::move(error)); + presence.close(); + this->send_stanza(presence); +} + +void XmppComponent::send_affiliation_role_change(const std::string& muc_name, + const std::string& target, + const std::string& affiliation, + const std::string& role, + const std::string& jid_to) +{ + Stanza presence("presence"); + presence["from"] = muc_name + "@" + this->served_hostname + "/" + target; + presence["to"] = jid_to; + XmlNode x("x"); + x["xmlns"] = MUC_USER_NS; + XmlNode item("item"); + item["affiliation"] = affiliation; + item["role"] = role; + item.close(); + x.add_child(std::move(item)); + x.close(); + presence.add_child(std::move(x)); + presence.close(); + this->send_stanza(presence); +} + +void XmppComponent::send_version(const std::string& id, const std::string& jid_to, const std::string& jid_from, + const std::string& version) +{ + Stanza iq("iq"); + iq["type"] = "result"; + iq["id"] = id; + iq["to"] = jid_to; + iq["from"] = jid_from; + XmlNode query("query"); + query["xmlns"] = VERSION_NS; + if (version.empty()) + { + XmlNode name("name"); + name.set_inner("biboumi"); + name.close(); + query.add_child(std::move(name)); + XmlNode version("version"); + version.set_inner(SOFTWARE_VERSION); + version.close(); + query.add_child(std::move(version)); + XmlNode os("os"); + os.set_inner(SYSTEM_NAME); + os.close(); + query.add_child(std::move(os)); + } + else + { + XmlNode name("name"); + name.set_inner(version); + name.close(); + query.add_child(std::move(name)); + } + query.close(); + iq.add_child(std::move(query)); + iq.close(); + this->send_stanza(iq); +} + +void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid) +{ + Stanza iq("iq"); + iq["type"] = "result"; + iq["id"] = id; + iq["to"] = requester_jid; + iq["from"] = this->served_hostname; + XmlNode query("query"); + query["xmlns"] = DISCO_ITEMS_NS; + query["node"] = ADHOC_NS; + for (const auto& kv: this->adhoc_commands_handler.get_commands()) + { + XmlNode item("item"); + item["jid"] = this->served_hostname; + item["node"] = kv.first; + item["name"] = kv.second.name; + item.close(); + query.add_child(std::move(item)); + } + query.close(); + iq.add_child(std::move(query)); + iq.close(); + this->send_stanza(iq); +} + +void XmppComponent::send_iq_version_request(const std::string& from, + const std::string& jid_to) +{ + Stanza iq("iq"); + iq["type"] = "get"; + iq["id"] = "version_"s + XmppComponent::next_id(); + iq["from"] = from + "@" + this->served_hostname; + iq["to"] = jid_to; + XmlNode query("query"); + query["xmlns"] = VERSION_NS; + query.close(); + iq.add_child(std::move(query)); + iq.close(); + this->send_stanza(iq); +} + +void XmppComponent::send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from_local_part) +{ + Stanza iq("iq"); + if (!from_local_part.empty()) + iq["from"] = from_local_part + "@" + this->served_hostname; + else + iq["from"] = this->served_hostname; + iq["to"] = to_jid; + iq["id"] = id; + iq["type"] = "result"; + iq.close(); + this->send_stanza(iq); +} + +std::string XmppComponent::next_id() +{ + char uuid_str[37]; + uuid_t uuid; + uuid_generate(uuid); + uuid_unparse(uuid, uuid_str); + return uuid_str; +} diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp new file mode 100644 index 0000000..1bea54e --- /dev/null +++ b/louloulibs/xmpp/xmpp_component.hpp @@ -0,0 +1,240 @@ +#ifndef XMPP_COMPONENT_INCLUDED +# define XMPP_COMPONENT_INCLUDED + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define STREAM_NS "http://etherx.jabber.org/streams" +#define COMPONENT_NS "jabber:component:accept" +#define MUC_NS "http://jabber.org/protocol/muc" +#define MUC_USER_NS MUC_NS"#user" +#define MUC_ADMIN_NS MUC_NS"#admin" +#define DISCO_NS "http://jabber.org/protocol/disco" +#define DISCO_ITEMS_NS DISCO_NS"#items" +#define DISCO_INFO_NS DISCO_NS"#info" +#define XHTMLIM_NS "http://jabber.org/protocol/xhtml-im" +#define STANZA_NS "urn:ietf:params:xml:ns:xmpp-stanzas" +#define STREAMS_NS "urn:ietf:params:xml:ns:xmpp-streams" +#define VERSION_NS "jabber:iq:version" +#define ADHOC_NS "http://jabber.org/protocol/commands" +#define PING_NS "urn:xmpp:ping" + +/** + * An XMPP component, communicating with an XMPP server using the protocole + * described in XEP-0114: Jabber Component Protocol + * + * TODO: implement XEP-0225: Component Connections + */ +class XmppComponent: public TCPSocketHandler +{ +public: + explicit XmppComponent(std::shared_ptr poller, const std::string& hostname, const std::string& secret); + virtual ~XmppComponent() = default; + + void on_connection_failed(const std::string& reason) override final; + void on_connected() override final; + void on_connection_close(const std::string& error) override final; + void parse_in_buffer(const size_t size) override final; + + /** + * Returns a unique id, to be used in the 'id' element of our iq stanzas. + */ + static std::string next_id(); + bool is_document_open() const; + /** + * Connect to the XMPP server. + */ + void start(); + /** + * Reset the component so we can use the component on a new XMPP stream + */ + void reset(); + /** + * Serialize the stanza and add it to the out_buf to be sent to the + * server. + */ + void send_stanza(const Stanza& stanza); + /** + * Handle the opening of the remote stream + */ + void on_remote_stream_open(const XmlNode& node); + /** + * Handle the closing of the remote stream + */ + void on_remote_stream_close(const XmlNode& node); + /** + * Handle received stanzas + */ + void on_stanza(const Stanza& stanza); + /** + * Send an error stanza. Message being the name of the element inside the + * stanza, and explanation being a short human-readable sentence + * describing the error. + */ + void send_stream_error(const std::string& message, const std::string& explanation); + /** + * Send error stanza, described in http://xmpp.org/rfcs/rfc6120.html#stanzas-error + */ + void send_stanza_error(const std::string& kind, const std::string& to, const std::string& from, + const std::string& id, const std::string& error_type, + const std::string& defined_condition, const std::string& text, + const bool fulljid=true); + /** + * Send the closing signal for our document (not closing the connection though). + */ + void close_document(); + /** + * Send a message from from@served_hostname, with the given body + * + * If fulljid is false, the provided 'from' doesn't contain the + * server-part of the JID and must be added. + */ + void send_message(const std::string& from, Xmpp::body&& body, + const std::string& to, const std::string& type, + const bool fulljid=false); + /** + * Send a join from a new participant + */ + void send_user_join(const std::string& from, + const std::string& nick, + const std::string& realjid, + const std::string& affiliation, + const std::string& role, + const std::string& to, + const bool self); + /** + * Send an error to indicate that the user tried to join an invalid room + */ + void send_invalid_room_error(const std::string& muc_jid, + const std::string& nick, + const std::string& to); + /** + * Send an error to indicate that the user tried to send a message to an + * invalid user. + */ + void send_invalid_user_error(const std::string& user_name, + const std::string& to); + /** + * Send the MUC topic to the user + */ + void send_topic(const std::string& from, Xmpp::body&& xmpp_topic, const std::string& to); + /** + * Send a (non-private) message to the MUC + */ + void send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& body, const std::string& jid_to); + /** + * Send an unavailable presence for this nick + */ + void send_muc_leave(const std::string& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self); + /** + * Indicate that a participant changed his nick + */ + void send_nick_change(const std::string& muc_name, + const std::string& old_nick, + const std::string& new_nick, + const std::string& affiliation, + const std::string& role, + const std::string& jid_to, + const bool self); + /** + * An user is kicked from a room + */ + void kick_user(const std::string& muc_name, + const std::string& target, + const std::string& reason, + const std::string& author, + const std::string& jid_to); + /** + * Send a generic presence error + */ + void send_presence_error(const std::string& muc_name, + const std::string& nickname, + const std::string& jid_to, + const std::string& type, + const std::string& condition, + const std::string& error_code, + const std::string& text); + /** + * Send a presence from the MUC indicating a change in the role and/or + * affiliation of a participant + */ + void send_affiliation_role_change(const std::string& muc_name, + const std::string& target, + const std::string& affiliation, + const std::string& role, + const std::string& jid_to); + /** + * Send a result IQ with the gateway disco informations. + */ + void send_self_disco_info(const std::string& id, const std::string& jid_to); + /** + * Send a result IQ with the given version, or the gateway version if the + * passed string is empty. + */ + void send_version(const std::string& id, const std::string& jid_to, const std::string& jid_from, + const std::string& version=""); + /** + * Send the list of all available ad-hoc commands to that JID. The list is + * different depending on what JID made the request. + */ + void send_adhoc_commands_list(const std::string& id, const std::string& requester_jid); + /** + * Send an iq version request + */ + void send_iq_version_request(const std::string& from, + const std::string& jid_to); + /** + * Send an empty iq of type result + */ + void send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from); + + void handle_handshake(const Stanza& stanza); + void handle_error(const Stanza& stanza); + + virtual void after_handshake() {} + + /** + * Whether or not we ever succeeded our authentication to the XMPP server + */ + bool ever_auth; + /** + * Whether or not this is the first consecutive try on connecting to the + * XMPP server. We use this to delay the connection attempt for a few + * seconds, if it is not the first try. + */ + bool first_connection_try; + +private: + /** + * Return a buffer provided by the XML parser, to read data directly into + * it, and avoiding some unnecessary copy. + */ + void* get_receive_buffer(const size_t size) const override final; + XmppParser parser; + std::string stream_id; + std::string secret; + bool authenticated; + /** + * Whether or not OUR XMPP document is open + */ + bool doc_open; +protected: + std::string served_hostname; + + std::unordered_map> stanza_handlers; + AdhocCommandsHandler adhoc_commands_handler; + + XmppComponent(const XmppComponent&) = delete; + XmppComponent(XmppComponent&&) = delete; + XmppComponent& operator=(const XmppComponent&) = delete; + XmppComponent& operator=(XmppComponent&&) = delete; +}; + +#endif // XMPP_COMPONENT_INCLUDED diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp new file mode 100644 index 0000000..6bb0d28 --- /dev/null +++ b/louloulibs/xmpp/xmpp_parser.cpp @@ -0,0 +1,169 @@ +#include +#include + +#include + +/** + * Expat handlers. Called by the Expat library, never by ourself. + * They just forward the call to the XmppParser corresponding methods. + */ + +static void start_element_handler(void* user_data, const XML_Char* name, const XML_Char** atts) +{ + static_cast(user_data)->start_element(name, atts); +} + +static void end_element_handler(void* user_data, const XML_Char* name) +{ + static_cast(user_data)->end_element(name); +} + +static void character_data_handler(void *user_data, const XML_Char *s, int len) +{ + static_cast(user_data)->char_data(s, len); +} + +/** + * XmppParser class + */ + +XmppParser::XmppParser(): + level(0), + current_node(nullptr) +{ + this->init_xml_parser(); +} + +void XmppParser::init_xml_parser() +{ + // Create the expat parser + this->parser = XML_ParserCreateNS("UTF-8", ':'); + XML_SetUserData(this->parser, static_cast(this)); + + // Install Expat handlers + XML_SetElementHandler(this->parser, &start_element_handler, &end_element_handler); + XML_SetCharacterDataHandler(this->parser, &character_data_handler); +} + +XmppParser::~XmppParser() +{ + if (this->current_node) + delete this->current_node; + XML_ParserFree(this->parser); +} + +int XmppParser::feed(const char* data, const int len, const bool is_final) +{ + int res = XML_Parse(this->parser, data, len, is_final); + if (res == XML_STATUS_ERROR && + (XML_GetErrorCode(this->parser) != XML_ERROR_FINISHED)) + log_error("Xml_Parse encountered an error: " << + XML_ErrorString(XML_GetErrorCode(this->parser))) + return res; +} + +int XmppParser::parse(const int len, const bool is_final) +{ + int res = XML_ParseBuffer(this->parser, len, is_final); + if (res == XML_STATUS_ERROR) + log_error("Xml_Parsebuffer encountered an error: " << + XML_ErrorString(XML_GetErrorCode(this->parser))); + return res; +} + +void XmppParser::reset() +{ + XML_ParserFree(this->parser); + this->init_xml_parser(); + if (this->current_node) + delete this->current_node; + this->current_node = nullptr; + this->level = 0; +} + +void* XmppParser::get_buffer(const size_t size) const +{ + return XML_GetBuffer(this->parser, static_cast(size)); +} + +void XmppParser::start_element(const XML_Char* name, const XML_Char** attribute) +{ + level++; + + XmlNode* new_node = new XmlNode(name, this->current_node); + if (this->current_node) + this->current_node->add_child(new_node); + this->current_node = new_node; + for (size_t i = 0; attribute[i]; i += 2) + this->current_node->set_attribute(attribute[i], attribute[i+1]); + if (this->level == 1) + this->stream_open_event(*this->current_node); +} + +void XmppParser::end_element(const XML_Char* name) +{ + (void)name; + level--; + this->current_node->close(); + if (level == 1) + { + this->stanza_event(*this->current_node); + } + if (level == 0) + { + this->stream_close_event(*this->current_node); + delete this->current_node; + this->current_node = nullptr; + } + else + this->current_node = this->current_node->get_parent(); + if (level == 1) + this->current_node->delete_all_children(); +} + +void XmppParser::char_data(const XML_Char* data, int len) +{ + if (this->current_node->has_children()) + this->current_node->get_last_child()->add_to_tail(std::string(data, len)); + else + this->current_node->add_to_inner(std::string(data, len)); +} + +void XmppParser::stanza_event(const Stanza& stanza) const +{ + for (const auto& callback: this->stanza_callbacks) + { + try { + callback(stanza); + } catch (const std::exception& e) { + log_debug("Unhandled exception: " << e.what()); + } + } +} + +void XmppParser::stream_open_event(const XmlNode& node) const +{ + for (const auto& callback: this->stream_open_callbacks) + callback(node); +} + +void XmppParser::stream_close_event(const XmlNode& node) const +{ + for (const auto& callback: this->stream_close_callbacks) + callback(node); +} + +void XmppParser::add_stanza_callback(std::function&& callback) +{ + this->stanza_callbacks.emplace_back(std::move(callback)); +} + +void XmppParser::add_stream_open_callback(std::function&& callback) +{ + this->stream_open_callbacks.emplace_back(std::move(callback)); +} + +void XmppParser::add_stream_close_callback(std::function&& callback) +{ + this->stream_close_callbacks.emplace_back(std::move(callback)); +} diff --git a/louloulibs/xmpp/xmpp_parser.hpp b/louloulibs/xmpp/xmpp_parser.hpp new file mode 100644 index 0000000..79c9f8f --- /dev/null +++ b/louloulibs/xmpp/xmpp_parser.hpp @@ -0,0 +1,126 @@ +#ifndef XMPP_PARSER_INCLUDED +# define XMPP_PARSER_INCLUDED + +#include + +#include + +#include + +/** + * A SAX XML parser that builds XML nodes and spawns events when a complete + * stanza is received (an element of level 2), or when the document is + * opened/closed (an element of level 1) + * + * After a stanza_event has been spawned, we delete the whole stanza. This + * means that even with a very long document (in XMPP the document is + * potentially infinite), the memory is never exhausted as long as each + * stanza is reasonnably short. + * + * The element names generated by expat contain the namespace of the + * element, a colon (':') and then the actual name of the element. To get + * an element "x" with a namespace of "http://jabber.org/protocol/muc", you + * just look for an XmlNode named "http://jabber.org/protocol/muc:x" + * + * TODO: enforce the size-limit for the stanza (limit the number of childs + * it can contain). For example forbid the parser going further than level + * 20 (arbitrary number here), and each XML node to have more than 15 childs + * (arbitrary number again). + */ +class XmppParser +{ +public: + explicit XmppParser(); + ~XmppParser(); + +public: + /** + * Init the XML parser and install the callbacks + */ + void init_xml_parser(); + /** + * Feed the parser with some XML data + */ + int feed(const char* data, const int len, const bool is_final); + /** + * Parse the data placed in the parser buffer + */ + int parse(const int size, const bool is_final); + /** + * Reset the parser, so it can be used from scratch afterward + */ + void reset(); + /** + * Get a buffer provided by the xml parser. + */ + void* get_buffer(const size_t size) const; + /** + * Add one callback for the various events that this parser can spawn. + */ + void add_stanza_callback(std::function&& callback); + void add_stream_open_callback(std::function&& callback); + void add_stream_close_callback(std::function&& callback); + + /** + * Called when a new XML element has been opened. We instanciate a new + * XmlNode and set it as our current node. The parent of this new node is + * the previous "current" node. We have all the element's attributes in + * this event. + * + * We spawn a stream_event with this node if this is a level-1 element. + */ + void start_element(const XML_Char* name, const XML_Char** attribute); + /** + * Called when an XML element has been closed. We close the current_node, + * set our current_node as the parent of the current_node, and if that was + * a level-2 element we spawn a stanza_event with this node. + * + * And we then delete the stanza (and everything under it, its children, + * attribute, etc). + */ + void end_element(const XML_Char* name); + /** + * Some inner or tail data has been parsed + */ + void char_data(const XML_Char* data, int len); + /** + * Calls all the stanza_callbacks one by one. + */ + void stanza_event(const Stanza& stanza) const; + /** + * Calls all the stream_open_callbacks one by one. Note: the passed node is not + * closed yet. + */ + void stream_open_event(const XmlNode& node) const; + /** + * Calls all the stream_close_callbacks one by one. + */ + void stream_close_event(const XmlNode& node) const; + +private: + /** + * Expat structure. + */ + XML_Parser parser; + /** + * The current depth in the XML document + */ + size_t level; + /** + * The deepest XML node opened but not yet closed (to which we are adding + * new children, inner or tail) + */ + XmlNode* current_node; + /** + * A list of callbacks to be called on an *_event, receiving the + * concerned Stanza/XmlNode. + */ + std::vector> stanza_callbacks; + std::vector> stream_open_callbacks; + std::vector> stream_close_callbacks; + + XmppParser(const XmppParser&) = delete; + XmppParser& operator=(const XmppParser&) = delete; +}; + +#endif // XMPP_PARSER_INCLUDED diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp new file mode 100644 index 0000000..01d1d2e --- /dev/null +++ b/louloulibs/xmpp/xmpp_stanza.cpp @@ -0,0 +1,274 @@ +#include + +#include +#include + +#include +#include + +#include + +std::string xml_escape(const std::string& data) +{ + std::string res; + res.reserve(data.size()); + for (size_t pos = 0; pos != data.size(); ++pos) + { + switch(data[pos]) + { + case '&': + res += "&"; + break; + case '<': + res += "<"; + break; + case '>': + res += ">"; + break; + case '\"': + res += """; + break; + case '\'': + res += "'"; + break; + default: + res += data[pos]; + break; + } + } + return res; +} + +std::string xml_unescape(const std::string& data) +{ + std::string res; + res.reserve(data.size()); + const char* str = data.c_str(); + while (str && *str && static_cast(str - data.c_str()) < data.size()) + { + if (*str == '&') + { + if (strncmp(str+1, "amp;", 4) == 0) + { + res += "&"; + str += 4; + } + else if (strncmp(str+1, "lt;", 3) == 0) + { + res += "<"; + str += 3; + } + else if (strncmp(str+1, "gt;", 3) == 0) + { + res += ">"; + str += 3; + } + else if (strncmp(str+1, "quot;", 5) == 0) + { + res += "\""; + str += 5; + } + else if (strncmp(str+1, "apos;", 5) == 0) + { + res += "'"; + str += 5; + } + else + res += "&"; + } + else + res += *str; + str++; + } + return res; +} + +XmlNode::XmlNode(const std::string& name, XmlNode* parent): + parent(parent), + closed(false) +{ + // split the namespace and the name + auto n = name.rfind(":"); + if (n == std::string::npos) + this->name = name; + else + { + this->name = name.substr(n+1); + this->attributes["xmlns"] = name.substr(0, n); + } +} + +XmlNode::XmlNode(const std::string& name): + XmlNode(name, nullptr) +{ +} + +XmlNode::~XmlNode() +{ + this->delete_all_children(); +} + +void XmlNode::delete_all_children() +{ + for (auto& child: this->children) + { + delete child; + } + this->children.clear(); +} + +void XmlNode::set_attribute(const std::string& name, const std::string& value) +{ + this->attributes[name] = value; +} + +void XmlNode::set_tail(const std::string& data) +{ + this->tail = data; +} + +void XmlNode::add_to_tail(const std::string& data) +{ + this->tail += data; +} + +void XmlNode::set_inner(const std::string& data) +{ + this->inner = data; +} + +void XmlNode::add_to_inner(const std::string& data) +{ + this->inner += data; +} + +std::string XmlNode::get_inner() const +{ + return this->inner; +} + +std::string XmlNode::get_tail() const +{ + return this->tail; +} + +XmlNode* XmlNode::get_child(const std::string& name, const std::string& xmlns) const +{ + for (auto& child: this->children) + { + if (child->name == name && child->get_tag("xmlns") == xmlns) + return child; + } + return nullptr; +} + +std::vector XmlNode::get_children(const std::string& name, const std::string& xmlns) const +{ + std::vector res; + for (auto& child: this->children) + { + if (child->name == name && child->get_tag("xmlns") == xmlns) + res.push_back(child); + } + return res; +} + +XmlNode* XmlNode::add_child(XmlNode* child) +{ + child->parent = this; + this->children.push_back(child); + return child; +} + +XmlNode* XmlNode::add_child(XmlNode&& child) +{ + XmlNode* new_node = new XmlNode(std::move(child)); + return this->add_child(new_node); +} + +XmlNode* XmlNode::get_last_child() const +{ + return this->children.back(); +} + +void XmlNode::close() +{ + if (this->closed) + throw std::runtime_error("Closing an already closed XmlNode"); + this->closed = true; +} + +XmlNode* XmlNode::get_parent() const +{ + return this->parent; +} + +void XmlNode::set_name(const std::string& name) +{ + this->name = name; +} + +const std::string XmlNode::get_name() const +{ + return this->name; +} + +std::string XmlNode::to_string() const +{ + std::string res("<"); + res += this->name; + for (const auto& it: this->attributes) + res += " " + it.first + "='" + sanitize(it.second) + "'"; + if (this->closed && !this->has_children() && this->inner.empty()) + res += "/>"; + else + { + res += ">" + sanitize(this->inner); + for (const auto& child: this->children) + res += child->to_string(); + if (this->closed) + { + res += "get_name() + ">"; + } + } + res += sanitize(this->tail); + return res; +} + +bool XmlNode::has_children() const +{ + return !this->children.empty(); +} + +const std::string XmlNode::get_tag(const std::string& name) const +{ + try + { + const auto& value = this->attributes.at(name); + return value; + } + catch (const std::out_of_range& e) + { + return ""; + } +} + +bool XmlNode::del_tag(const std::string& name) +{ + if (this->attributes.erase(name) != 0) + return true; + return false; +} + +std::string& XmlNode::operator[](const std::string& name) +{ + return this->attributes[name]; +} + +std::string sanitize(const std::string& data) +{ + if (utils::is_valid_utf8(data.data())) + return xml_escape(utils::remove_invalid_xml_chars(data)); + else + return xml_escape(utils::remove_invalid_xml_chars(utils::convert_to_utf8(data, "ISO-8859-1"))); +} diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp new file mode 100644 index 0000000..f1a6a0f --- /dev/null +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -0,0 +1,160 @@ +#ifndef XMPP_STANZA_INCLUDED +# define XMPP_STANZA_INCLUDED + +#include +#include +#include + +std::string xml_escape(const std::string& data); +std::string xml_unescape(const std::string& data); +std::string sanitize(const std::string& data); + +/** + * Represent an XML node. It has + * - A parent XML node (in the case of the first-level nodes, the parent is + nullptr) + * - zero, one or more children XML nodes + * - A name + * - A map of attributes + * - inner data (text inside the node) + * - tail data (text just after the node) + */ +class XmlNode +{ +public: + explicit XmlNode(const std::string& name, XmlNode* parent); + explicit XmlNode(const std::string& name); + XmlNode(XmlNode&& node): + name(std::move(node.name)), + parent(node.parent), + closed(node.closed), + attributes(std::move(node.attributes)), + children(std::move(node.children)), + inner(std::move(node.inner)), + tail(std::move(node.tail)) + { + node.parent = nullptr; + } + /** + * The copy constructor do not copy the parent attribute. The children + * nodes are all copied recursively. + */ + XmlNode(const XmlNode& node): + name(node.name), + parent(nullptr), + closed(node.closed), + attributes(node.attributes), + children{}, + inner(node.inner), + tail(node.tail) + { + for (XmlNode* child: node.children) + { + XmlNode* child_copy = new XmlNode(*child); + this->add_child(child_copy); + } + } + + ~XmlNode(); + + void delete_all_children(); + void set_attribute(const std::string& name, const std::string& value); + /** + * Set the content of the tail, that is the text just after this node + */ + void set_tail(const std::string& data); + /** + * Append the given data to the content of the tail. This exists because + * the expat library may provide the complete text of an element in more + * than one call + */ + void add_to_tail(const std::string& data); + /** + * Set the content of the inner, that is the text inside this node. + */ + void set_inner(const std::string& data); + /** + * Append the given data to the content of the inner. For the reason + * described in add_to_tail comment. + */ + void add_to_inner(const std::string& data); + /** + * Get the content of inner + */ + std::string get_inner() const; + /** + * Get the content of the tail + */ + std::string get_tail() const; + /** + * Get a pointer to the first child element with that name and that xml namespace + */ + XmlNode* get_child(const std::string& name, const std::string& xmlns) const; + /** + * Get a vector of all the children that have that name and that xml namespace. + */ + std::vector get_children(const std::string& name, const std::string& xmlns) const; + /** + * Add a node child to this node. Assign this node to the child’s parent. + * Returns a pointer to the newly added child. + */ + XmlNode* add_child(XmlNode* child); + XmlNode* add_child(XmlNode&& child); + /** + * Returns the last of the children. If the node doesn't have any child, + * the behaviour is undefined. The user should make sure this is the case + * by calling has_children() for example. + */ + XmlNode* get_last_child() const; + /** + * Mark this node as closed, nothing else + */ + void close(); + XmlNode* get_parent() const; + void set_name(const std::string& name); + const std::string get_name() const; + /** + * Serialize the stanza into a string + */ + std::string to_string() const; + /** + * Whether or not this node has at least one child (if not, this is a leaf + * node) + */ + bool has_children() const; + /** + * Gets the value for the given attribute, returns an empty string if the + * node as no such attribute. + */ + const std::string get_tag(const std::string& name) const; + /** + * Remove the attribute of the node. Does nothing if that attribute is not + * present. Returns true if the tag was removed, false if it was absent. + */ + bool del_tag(const std::string& name); + /** + * Use this to set an attribute's value, like node["id"] = "12"; + */ + std::string& operator[](const std::string& name); + +private: + std::string name; + XmlNode* parent; + bool closed; + std::unordered_map attributes; + std::vector children; + std::string inner; + std::string tail; + + XmlNode& operator=(const XmlNode&) = delete; + XmlNode& operator=(XmlNode&&) = delete; +}; + +/** + * An XMPP stanza is just an XML node of level 2 in the XMPP document (the + * level 1 ones are the , and the ones above 2 are just the + * content of the stanzas) + */ +typedef XmlNode Stanza; + +#endif // XMPP_STANZA_INCLUDED -- cgit v1.2.3 From 7f7c429ae4c49e856a43816138991135ffb7f840 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 9 Jun 2015 14:52:46 +0200 Subject: Do not send the admin-only adhoc commands to non-admin users They were not able to execute them anyway, so this was just a little usability issue. --- louloulibs/xmpp/xmpp_component.cpp | 4 +++- louloulibs/xmpp/xmpp_component.hpp | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index 1048f86..8e89208 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -651,7 +651,7 @@ void XmppComponent::send_version(const std::string& id, const std::string& jid_t this->send_stanza(iq); } -void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid) +void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, const bool with_admin_only) { Stanza iq("iq"); iq["type"] = "result"; @@ -663,6 +663,8 @@ void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::s query["node"] = ADHOC_NS; for (const auto& kv: this->adhoc_commands_handler.get_commands()) { + if (kv.second.is_admin_only() && !with_admin_only) + continue; XmlNode item("item"); item["jid"] = this->served_hostname; item["node"] = kv.first; diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp index 1bea54e..e45bb36 100644 --- a/louloulibs/xmpp/xmpp_component.hpp +++ b/louloulibs/xmpp/xmpp_component.hpp @@ -184,7 +184,8 @@ public: * Send the list of all available ad-hoc commands to that JID. The list is * different depending on what JID made the request. */ - void send_adhoc_commands_list(const std::string& id, const std::string& requester_jid); + void send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, + const bool with_admin_only); /** * Send an iq version request */ -- cgit v1.2.3 From 0af536a2be6d23fa221f61a77e89436b8fb2a015 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 8 Jul 2015 18:51:05 +0200 Subject: Minor comment typo --- louloulibs/network/tcp_socket_handler.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 2b5cd49..0dde2cf 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -272,11 +272,11 @@ private: static Botan::TLS::Session_Manager_In_Memory session_manager; /** * We use a unique_ptr because we may not want to create the object at - * all. The Botan::TLS::Client object generates a handshake message as - * soon and calls the output_fn callback with it as soon as it is - * created. Therefore, we do not want to create it if do not intend to do - * send any TLS-encrypted message. We create the object only when needed - * (for example after we have negociated a TLS session using a STARTTLS + * all. The Botan::TLS::Client object generates a handshake message and + * calls the output_fn callback with it as soon as it is created. + * Therefore, we do not want to create it if we do not intend to send any + * TLS-encrypted message. We create the object only when needed (for + * example after we have negociated a TLS session using a STARTTLS * message, or stuf like that). * * See start_tls for the method where this object is created. -- cgit v1.2.3 From a42af9e700bcf54777b53b5d0ca999b3b0c107e9 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 9 Jul 2015 13:49:08 +0200 Subject: Fix the log_* macros to not have two ; at the end of lines --- louloulibs/logger/logger.hpp | 8 ++++---- louloulibs/xmpp/xmpp_parser.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/logger/logger.hpp b/louloulibs/logger/logger.hpp index 78b1278..ab8bdf1 100644 --- a/louloulibs/logger/logger.hpp +++ b/louloulibs/logger/logger.hpp @@ -37,16 +37,16 @@ __FILENAME__ << ":" << __LINE__ #define log_debug(text)\ - Logger::instance()->get_stream(debug_lvl) << SD_DEBUG << WHERE << ":\t" << text << std::endl; + Logger::instance()->get_stream(debug_lvl) << SD_DEBUG << WHERE << ":\t" << text << std::endl #define log_info(text)\ - Logger::instance()->get_stream(info_lvl) << SD_INFO << WHERE << ":\t" << text << std::endl; + Logger::instance()->get_stream(info_lvl) << SD_INFO << WHERE << ":\t" << text << std::endl #define log_warning(text)\ - Logger::instance()->get_stream(warning_lvl) << SD_WARNING << WHERE << ":\t" << text << std::endl; + Logger::instance()->get_stream(warning_lvl) << SD_WARNING << WHERE << ":\t" << text << std::endl #define log_error(text)\ - Logger::instance()->get_stream(error_lvl) << SD_ERR << WHERE << ":\t" << text << std::endl; + Logger::instance()->get_stream(error_lvl) << SD_ERR << WHERE << ":\t" << text << std::endl /** * Juste a structure representing a stream doing nothing with its input. diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp index 6bb0d28..fbe525a 100644 --- a/louloulibs/xmpp/xmpp_parser.cpp +++ b/louloulibs/xmpp/xmpp_parser.cpp @@ -58,7 +58,7 @@ int XmppParser::feed(const char* data, const int len, const bool is_final) if (res == XML_STATUS_ERROR && (XML_GetErrorCode(this->parser) != XML_ERROR_FINISHED)) log_error("Xml_Parse encountered an error: " << - XML_ErrorString(XML_GetErrorCode(this->parser))) + XML_ErrorString(XML_GetErrorCode(this->parser))); return res; } -- cgit v1.2.3 From 4130f1679d909e90d691d47d07cd0e0d4fbe64e1 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 9 Jul 2015 13:54:24 +0200 Subject: Check for timeouts on the DNS resolution using c-ares ref #3083 --- louloulibs/network/dns_handler.cpp | 30 ++++++++++++++++++++++++++++-- louloulibs/network/dns_handler.hpp | 4 ++-- 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index ec53683..324784b 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -30,7 +32,13 @@ DNSHandler::DNSHandler() int ares_error; if ((ares_error = ::ares_library_init(ARES_LIB_INIT_ALL)) != 0) throw std::runtime_error("Failed to initialize c-ares lib: "s + ares_strerror(ares_error)); - if ((ares_error = ::ares_init(&this->channel)) != ARES_SUCCESS) + struct ares_options options = {}; + // The default timeout values are way too high + options.timeout = 1000; + options.tries = 3; + if ((ares_error = ::ares_init_options(&this->channel, + &options, + ARES_OPT_TIMEOUTMS|ARES_OPT_TRIES)) != ARES_SUCCESS) throw std::runtime_error("Failed to initialize c-ares channel: "s + ares_strerror(ares_error)); } @@ -99,7 +107,8 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) { // If not found, create it because we need to watch it if (it == this->socket_handlers.end()) { - this->socket_handlers.emplace_front(std::make_unique(poller, i)); + this->socket_handlers.emplace(this->socket_handlers.begin(), + std::make_unique(poller, i)); it = this->socket_handlers.begin(); } poller->add_socket_handler(it->get()); @@ -107,6 +116,23 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) poller->watch_send_events(it->get()); } } + // Cancel previous timer, if any. + TimedEventsManager::instance().cancel("DNS timeout"); + struct timeval tv; + struct timeval* tvp; + tvp = ::ares_timeout(this->channel, NULL, &tv); + if (tvp) + { + auto future_time = std::chrono::steady_clock::now() + std::chrono::seconds(tvp->tv_sec) + \ + std::chrono::microseconds(tvp->tv_usec); + TimedEventsManager::instance().add_event(TimedEvent(std::move(future_time), + [this]() + { + for (auto& dns_socket_handler: this->socket_handlers) + dns_socket_handler->on_recv(); + }, + "DNS timeout")); + } } #endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index a515f52..ec35374 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -11,7 +11,7 @@ class DNSSocketHandler; # include # include # include -# include +# include void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent); void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent); @@ -49,7 +49,7 @@ private: * call to ares_fds. DNSSocketHandlers are added to it or removed from it * in the watch_dns_sockets() method */ - std::list> socket_handlers; + std::vector> socket_handlers; ares_channel channel; DNSHandler(const DNSHandler&) = delete; -- cgit v1.2.3 From ac6a74595c679c22ae13eca0609a745e431b339c Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 9 Jul 2015 13:56:22 +0200 Subject: Send the cares error message before cleaning it fix #3083 --- louloulibs/network/tcp_socket_handler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index f647b86..e20357b 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -109,8 +109,9 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po addr_res = this->cares_addrinfo; if (!addr_res) { + const auto msg = this->cares_error; this->close(); - this->on_connection_failed(this->cares_error); + this->on_connection_failed(msg); return ; } } -- cgit v1.2.3 From ece4b4969b296a3da010fb22768348650e70962d Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 9 Jul 2015 15:30:24 +0200 Subject: If hostname resolution fails, do not try all possible ports --- louloulibs/network/tcp_socket_handler.cpp | 9 ++++++--- louloulibs/network/tcp_socket_handler.hpp | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index e20357b..04bb321 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -40,15 +40,16 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): SocketHandler(poller, -1), use_tls(false), connected(false), - connecting(false) + connecting(false), #ifdef CARES_FOUND - ,resolving(false), + resolving(false), resolved(false), resolved4(false), resolved6(false), cares_addrinfo(nullptr), - cares_error() + cares_error(), #endif + hostname_resolution_failed(false) {} TCPSocketHandler::~TCPSocketHandler() @@ -109,6 +110,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po addr_res = this->cares_addrinfo; if (!addr_res) { + this->hostname_resolution_failed = true; const auto msg = this->cares_error; this->close(); this->on_connection_failed(msg); @@ -129,6 +131,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po if (res != 0) { log_warning("getaddrinfo failed: "s + gai_strerror(res)); + this->hostname_resolution_failed = true; this->close(); this->on_connection_failed(gai_strerror(res)); return ; diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 0dde2cf..8069825 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -256,6 +256,8 @@ protected: std::string cares_error; #endif // CARES_FOUND + bool hostname_resolution_failed; + private: TCPSocketHandler(const TCPSocketHandler&) = delete; TCPSocketHandler(TCPSocketHandler&&) = delete; -- cgit v1.2.3 From 45fd41eb38ce9cd0c5fd14d7278514b79d7debcd Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 9 Jul 2015 15:30:54 +0200 Subject: Display the resolved IP in debug logs --- louloulibs/network/tcp_socket_handler.cpp | 17 +++++++++++++++++ louloulibs/network/tcp_socket_handler.hpp | 5 +++++ 2 files changed, 22 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 04bb321..cca6cd2 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -16,6 +16,7 @@ #include #include +#include #ifdef BOTAN_FOUND # include @@ -159,6 +160,9 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po break; } } + + this->display_resolved_ip(rp); + if (::connect(this->socket, rp->ai_addr, rp->ai_addrlen) == 0 || errno == EISCONN) { @@ -343,6 +347,19 @@ void TCPSocketHandler::close() this->port.clear(); } +void TCPSocketHandler::display_resolved_ip(struct addrinfo* rp) const +{ + char buf[INET6_ADDRSTRLEN]; + if (rp->ai_family == AF_INET) + log_debug("Connecting to IP address " << ::inet_ntop(rp->ai_family, + &reinterpret_cast(rp->ai_addr)->sin_addr, + buf, sizeof(buf))); + else if (rp->ai_family == AF_INET6) + log_debug("Connecting to IPv6 address " << ::inet_ntop(rp->ai_family, + &reinterpret_cast(rp->ai_addr)->sin6_addr, + buf, sizeof(buf))); +} + void TCPSocketHandler::send_data(std::string&& data) { #ifdef BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 8069825..835c509 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -264,6 +264,11 @@ private: TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; TCPSocketHandler& operator=(TCPSocketHandler&&) = delete; + /** + * Display the resolved IP, just for information purpose. + */ + void display_resolved_ip(struct addrinfo* rp) const; + #ifdef BOTAN_FOUND /** * Botan stuff to manipulate a TLS session. -- cgit v1.2.3 From e8f22efe34415db0e1e5cb94635b089b18efe055 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Sep 2015 04:42:12 +0200 Subject: XmlNodes are now always closed Remove the close() method and closed attribute. Remove all the calls to close(). (Save one bool per XmlNode, yay, and save a few ifs and some useless function calls. At best it should be unnoticeably faster and lighter and save a few keystrokes in the future) --- louloulibs/xmpp/adhoc_command.cpp | 10 ---- louloulibs/xmpp/adhoc_commands_handler.cpp | 10 ---- louloulibs/xmpp/xmpp_component.cpp | 78 +++--------------------------- louloulibs/xmpp/xmpp_parser.cpp | 1 - louloulibs/xmpp/xmpp_stanza.cpp | 17 ++----- louloulibs/xmpp/xmpp_stanza.hpp | 7 --- 6 files changed, 9 insertions(+), 114 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/adhoc_command.cpp b/louloulibs/xmpp/adhoc_command.cpp index 24145f5..c17f1d9 100644 --- a/louloulibs/xmpp/adhoc_command.cpp +++ b/louloulibs/xmpp/adhoc_command.cpp @@ -25,7 +25,6 @@ void PingStep1(XmppComponent*, AdhocSession&, XmlNode& command_node) XmlNode note("note"); note["type"] = "info"; note.set_inner("Pong"); - note.close(); command_node.add_child(std::move(note)); } @@ -35,22 +34,17 @@ void HelloStep1(XmppComponent*, AdhocSession&, XmlNode& command_node) x["type"] = "form"; XmlNode title("title"); title.set_inner("Configure your name."); - title.close(); x.add_child(std::move(title)); XmlNode instructions("instructions"); instructions.set_inner("Please provide your name."); - instructions.close(); x.add_child(std::move(instructions)); XmlNode name_field("field"); name_field["var"] = "name"; name_field["type"] = "text-single"; name_field["label"] = "Your name"; XmlNode required("required"); - required.close(); name_field.add_child(std::move(required)); - name_field.close(); x.add_child(std::move(name_field)); - x.close(); command_node.add_child(std::move(x)); } @@ -75,7 +69,6 @@ void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node) XmlNode note("note"); note["type"] = "info"; note.set_inner("Hello "s + value->get_inner() + "!"s); - note.close(); command_node.delete_all_children(); command_node.add_child(std::move(note)); return; @@ -86,9 +79,7 @@ void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node) XmlNode error(ADHOC_NS":error"); error["type"] = "modify"; XmlNode condition(STANZA_NS":bad-request"); - condition.close(); error.add_child(std::move(condition)); - error.close(); command_node.add_child(std::move(error)); session.terminate(); } @@ -100,6 +91,5 @@ void Reload(XmppComponent*, AdhocSession&, XmlNode& command_node) XmlNode note("note"); note["type"] = "info"; note.set_inner("Configuration reloaded."); - note.close(); command_node.add_child(std::move(note)); } diff --git a/louloulibs/xmpp/adhoc_commands_handler.cpp b/louloulibs/xmpp/adhoc_commands_handler.cpp index 46c8a32..458a22c 100644 --- a/louloulibs/xmpp/adhoc_commands_handler.cpp +++ b/louloulibs/xmpp/adhoc_commands_handler.cpp @@ -36,9 +36,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, Xm XmlNode error(ADHOC_NS":error"); error["type"] = "cancel"; XmlNode condition(STANZA_NS":item-not-found"); - condition.close(); error.add_child(std::move(condition)); - error.close(); command_node.add_child(std::move(error)); } else if (command_it->second.is_admin_only() && @@ -47,9 +45,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, Xm XmlNode error(ADHOC_NS":error"); error["type"] = "cancel"; XmlNode condition(STANZA_NS":forbidden"); - condition.close(); error.add_child(std::move(condition)); - error.close(); command_node.add_child(std::move(error)); } else @@ -72,9 +68,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, Xm XmlNode error(ADHOC_NS":error"); error["type"] = "modify"; XmlNode condition(STANZA_NS":bad-request"); - condition.close(); error.add_child(std::move(condition)); - error.close(); command_node.add_child(std::move(error)); } else if (action == "execute" || action == "next" || action == "complete") @@ -95,9 +89,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, Xm command_node["status"] = "executing"; XmlNode actions("actions"); XmlNode next("next"); - next.close(); actions.add_child(std::move(next)); - actions.close(); command_node.add_child(std::move(actions)); } } @@ -112,9 +104,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, Xm XmlNode error(ADHOC_NS":error"); error["type"] = "modify"; XmlNode condition(STANZA_NS":bad-request"); - condition.close(); error.add_child(std::move(condition)); - error.close(); command_node.add_child(std::move(error)); } } diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index 8e89208..19111ba 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -89,12 +89,9 @@ void XmppComponent::on_connected() { log_info("connected to XMPP server"); this->first_connection_try = true; - XmlNode node("", nullptr); - node.set_name("stream:stream"); - node["xmlns"] = COMPONENT_NS; - node["xmlns:stream"] = STREAM_NS; - node["to"] = this->served_hostname; - this->send_stanza(node); + this->send_data(""); this->doc_open = true; // We may have some pending data to send: this happens when we try to send // some data before we are actually connected. We send that data right now, if any @@ -152,10 +149,9 @@ void XmppComponent::on_remote_stream_open(const XmlNode& node) sprintf(digest + (i*2), "%02x", result[i]); digest[HASH_LENGTH * 2] = '\0'; - Stanza handshake(COMPONENT_NS":handshake"); - handshake.set_inner(digest); - handshake.close(); - this->send_stanza(handshake); + this->send_data(""); + this->send_data(digest); + this->send_data(""); } void XmppComponent::on_remote_stream_close(const XmlNode& node) @@ -192,9 +188,7 @@ void XmppComponent::send_stream_error(const std::string& name, const std::string error["xmlns"] = STREAM_NS; if (!explanation.empty()) error.set_inner(explanation); - error.close(); node.add_child(std::move(error)); - node.close(); this->send_stanza(node); } @@ -220,19 +214,15 @@ void XmppComponent::send_stanza_error(const std::string& kind, const std::string error["type"] = error_type; XmlNode inner_error(defined_condition); inner_error["xmlns"] = STANZA_NS; - inner_error.close(); error.add_child(std::move(inner_error)); if (!text.empty()) { XmlNode text_node("text"); text_node["xmlns"] = STANZA_NS; text_node.set_inner(text); - text_node.close(); error.add_child(std::move(text_node)); } - error.close(); node.add_child(std::move(error)); - node.close(); this->send_stanza(node); } @@ -295,7 +285,6 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con node["type"] = type; XmlNode body_node("body"); body_node.set_inner(std::get<0>(body)); - body_node.close(); node.add_child(std::move(body_node)); if (std::get<1>(body)) { @@ -303,10 +292,8 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con html["xmlns"] = XHTMLIM_NS; // Pass the ownership of the pointer to this xmlnode html.add_child(std::get<1>(body).release()); - html.close(); node.add_child(std::move(html)); } - node.close(); this->send_stanza(node); } @@ -336,19 +323,15 @@ void XmppComponent::send_user_join(const std::string& from, if (!preped_jid.empty()) item["jid"] = preped_jid; } - item.close(); x.add_child(std::move(item)); if (self) { XmlNode status("status"); status["code"] = "110"; - status.close(); x.add_child(std::move(status)); } - x.close(); node.add_child(std::move(x)); - node.close(); this->send_stanza(node); } @@ -365,14 +348,12 @@ void XmppComponent::send_invalid_room_error(const std::string& muc_name, presence["type"] = "error"; XmlNode x("x"); x["xmlns"] = MUC_NS; - x.close(); presence.add_child(std::move(x)); XmlNode error("error"); error["by"] = muc_name + "@" + this->served_hostname; error["type"] = "cancel"; XmlNode item_not_found("item-not-found"); item_not_found["xmlns"] = STANZA_NS; - item_not_found.close(); error.add_child(std::move(item_not_found)); XmlNode text("text"); text["xmlns"] = STANZA_NS; @@ -380,11 +361,8 @@ void XmppComponent::send_invalid_room_error(const std::string& muc_name, text.set_inner(muc_name + " is not a valid IRC channel name. A correct room jid is of the form: #%@" + this->served_hostname); - text.close(); error.add_child(std::move(text)); - error.close(); presence.add_child(std::move(error)); - presence.close(); this->send_stanza(presence); } @@ -396,13 +374,11 @@ void XmppComponent::send_invalid_user_error(const std::string& user_name, const message["type"] = "error"; XmlNode x("x"); x["xmlns"] = MUC_NS; - x.close(); message.add_child(std::move(x)); XmlNode error("error"); error["type"] = "cancel"; XmlNode item_not_found("item-not-found"); item_not_found["xmlns"] = STANZA_NS; - item_not_found.close(); error.add_child(std::move(item_not_found)); XmlNode text("text"); text["xmlns"] = STANZA_NS; @@ -410,11 +386,8 @@ void XmppComponent::send_invalid_user_error(const std::string& user_name, const text.set_inner(user_name + " is not a valid IRC user name. A correct user jid is of the form: !@" + this->served_hostname); - text.close(); error.add_child(std::move(text)); - error.close(); message.add_child(std::move(error)); - message.close(); this->send_stanza(message); } @@ -426,9 +399,7 @@ void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, cons message["type"] = "groupchat"; XmlNode subject("subject"); subject.set_inner(std::get<0>(topic)); - subject.close(); message.add_child(std::move(subject)); - message.close(); this->send_stanza(message); } @@ -443,7 +414,6 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str message["type"] = "groupchat"; XmlNode body("body"); body.set_inner(std::get<0>(xmpp_body)); - body.close(); message.add_child(std::move(body)); if (std::get<1>(xmpp_body)) { @@ -451,10 +421,8 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str html["xmlns"] = XHTMLIM_NS; // Pass the ownership of the pointer to this xmlnode html.add_child(std::get<1>(xmpp_body).release()); - html.close(); message.add_child(std::move(html)); } - message.close(); this->send_stanza(message); } @@ -471,19 +439,15 @@ void XmppComponent::send_muc_leave(const std::string& muc_name, std::string&& ni { XmlNode status("status"); status["code"] = "110"; - status.close(); x.add_child(std::move(status)); } - x.close(); presence.add_child(std::move(x)); if (!message_str.empty()) { XmlNode status("status"); status.set_inner(message_str); - status.close(); presence.add_child(std::move(status)); } - presence.close(); this->send_stanza(presence); } @@ -503,22 +467,17 @@ void XmppComponent::send_nick_change(const std::string& muc_name, x["xmlns"] = MUC_USER_NS; XmlNode item("item"); item["nick"] = new_nick; - item.close(); x.add_child(std::move(item)); XmlNode status("status"); status["code"] = "303"; - status.close(); x.add_child(std::move(status)); if (self) { XmlNode status2("status"); status2["code"] = "110"; - status2.close(); x.add_child(std::move(status2)); } - x.close(); presence.add_child(std::move(x)); - presence.close(); this->send_stanza(presence); this->send_user_join(muc_name, new_nick, "", affiliation, role, jid_to, self); @@ -542,21 +501,15 @@ void XmppComponent::kick_user(const std::string& muc_name, XmlNode actor("actor"); actor["nick"] = author; actor["jid"] = author; // backward compatibility with old clients - actor.close(); item.add_child(std::move(actor)); XmlNode reason("reason"); reason.set_inner(txt); - reason.close(); item.add_child(std::move(reason)); - item.close(); x.add_child(std::move(item)); XmlNode status("status"); status["code"] = "307"; - status.close(); x.add_child(std::move(status)); - x.close(); presence.add_child(std::move(x)); - presence.close(); this->send_stanza(presence); } @@ -574,7 +527,6 @@ void XmppComponent::send_presence_error(const std::string& muc_name, presence["type"] = "error"; XmlNode x("x"); x["xmlns"] = MUC_NS; - x.close(); presence.add_child(std::move(x)); XmlNode error("error"); error["by"] = muc_name + "@" + this->served_hostname; @@ -583,11 +535,8 @@ void XmppComponent::send_presence_error(const std::string& muc_name, error["code"] = error_code; XmlNode subnode(condition); subnode["xmlns"] = STANZA_NS; - subnode.close(); error.add_child(std::move(subnode)); - error.close(); presence.add_child(std::move(error)); - presence.close(); this->send_stanza(presence); } @@ -605,11 +554,8 @@ void XmppComponent::send_affiliation_role_change(const std::string& muc_name, XmlNode item("item"); item["affiliation"] = affiliation; item["role"] = role; - item.close(); x.add_child(std::move(item)); - x.close(); presence.add_child(std::move(x)); - presence.close(); this->send_stanza(presence); } @@ -627,27 +573,21 @@ void XmppComponent::send_version(const std::string& id, const std::string& jid_t { XmlNode name("name"); name.set_inner("biboumi"); - name.close(); query.add_child(std::move(name)); XmlNode version("version"); version.set_inner(SOFTWARE_VERSION); - version.close(); query.add_child(std::move(version)); XmlNode os("os"); os.set_inner(SYSTEM_NAME); - os.close(); query.add_child(std::move(os)); } else { XmlNode name("name"); name.set_inner(version); - name.close(); query.add_child(std::move(name)); } - query.close(); iq.add_child(std::move(query)); - iq.close(); this->send_stanza(iq); } @@ -669,12 +609,9 @@ void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::s item["jid"] = this->served_hostname; item["node"] = kv.first; item["name"] = kv.second.name; - item.close(); query.add_child(std::move(item)); } - query.close(); iq.add_child(std::move(query)); - iq.close(); this->send_stanza(iq); } @@ -688,9 +625,7 @@ void XmppComponent::send_iq_version_request(const std::string& from, iq["to"] = jid_to; XmlNode query("query"); query["xmlns"] = VERSION_NS; - query.close(); iq.add_child(std::move(query)); - iq.close(); this->send_stanza(iq); } @@ -704,7 +639,6 @@ void XmppComponent::send_iq_result(const std::string& id, const std::string& to_ iq["to"] = to_jid; iq["id"] = id; iq["type"] = "result"; - iq.close(); this->send_stanza(iq); } diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp index fbe525a..3903b73 100644 --- a/louloulibs/xmpp/xmpp_parser.cpp +++ b/louloulibs/xmpp/xmpp_parser.cpp @@ -104,7 +104,6 @@ void XmppParser::end_element(const XML_Char* name) { (void)name; level--; - this->current_node->close(); if (level == 1) { this->stanza_event(*this->current_node); diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp index 01d1d2e..c66b4be 100644 --- a/louloulibs/xmpp/xmpp_stanza.cpp +++ b/louloulibs/xmpp/xmpp_stanza.cpp @@ -84,8 +84,7 @@ std::string xml_unescape(const std::string& data) } XmlNode::XmlNode(const std::string& name, XmlNode* parent): - parent(parent), - closed(false) + parent(parent) { // split the namespace and the name auto n = name.rfind(":"); @@ -191,13 +190,6 @@ XmlNode* XmlNode::get_last_child() const return this->children.back(); } -void XmlNode::close() -{ - if (this->closed) - throw std::runtime_error("Closing an already closed XmlNode"); - this->closed = true; -} - XmlNode* XmlNode::get_parent() const { return this->parent; @@ -219,17 +211,14 @@ std::string XmlNode::to_string() const res += this->name; for (const auto& it: this->attributes) res += " " + it.first + "='" + sanitize(it.second) + "'"; - if (this->closed && !this->has_children() && this->inner.empty()) + if (!this->has_children() && this->inner.empty()) res += "/>"; else { res += ">" + sanitize(this->inner); for (const auto& child: this->children) res += child->to_string(); - if (this->closed) - { - res += "get_name() + ">"; - } + res += "get_name() + ">"; } res += sanitize(this->tail); return res; diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp index f1a6a0f..3d5b0c5 100644 --- a/louloulibs/xmpp/xmpp_stanza.hpp +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -27,7 +27,6 @@ public: XmlNode(XmlNode&& node): name(std::move(node.name)), parent(node.parent), - closed(node.closed), attributes(std::move(node.attributes)), children(std::move(node.children)), inner(std::move(node.inner)), @@ -42,7 +41,6 @@ public: XmlNode(const XmlNode& node): name(node.name), parent(nullptr), - closed(node.closed), attributes(node.attributes), children{}, inner(node.inner), @@ -106,10 +104,6 @@ public: * by calling has_children() for example. */ XmlNode* get_last_child() const; - /** - * Mark this node as closed, nothing else - */ - void close(); XmlNode* get_parent() const; void set_name(const std::string& name); const std::string get_name() const; @@ -140,7 +134,6 @@ public: private: std::string name; XmlNode* parent; - bool closed; std::unordered_map attributes; std::vector children; std::string inner; -- cgit v1.2.3 From d7e1214cbcff2d34f45687eff7c083a89bf04802 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Sep 2015 04:53:12 +0200 Subject: XmlNode::to_string uses an ostringstream instead of a string On my poor benchmark, it was infinitesimally faster. --- louloulibs/xmpp/xmpp_stanza.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp index c66b4be..aec91a7 100644 --- a/louloulibs/xmpp/xmpp_stanza.cpp +++ b/louloulibs/xmpp/xmpp_stanza.cpp @@ -5,6 +5,7 @@ #include #include +#include #include @@ -207,21 +208,21 @@ const std::string XmlNode::get_name() const std::string XmlNode::to_string() const { - std::string res("<"); - res += this->name; + std::ostringstream res; + res << "<" << this->name; for (const auto& it: this->attributes) - res += " " + it.first + "='" + sanitize(it.second) + "'"; + res << " " << it.first << "='" << sanitize(it.second) + "'"; if (!this->has_children() && this->inner.empty()) - res += "/>"; + res << "/>"; else { - res += ">" + sanitize(this->inner); + res << ">" + sanitize(this->inner); for (const auto& child: this->children) - res += child->to_string(); - res += "get_name() + ">"; + res << child->to_string(); + res << "get_name() << ">"; } - res += sanitize(this->tail); - return res; + res << sanitize(this->tail); + return res.str(); } bool XmlNode::has_children() const -- cgit v1.2.3 From 38564d77c7679dd4de4562d321146322b6211d61 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Sep 2015 04:56:06 +0200 Subject: Little cleanup of the XmlNode class Use map instead of unordered map (it's not slower, and it's shorter). Use the default move constructor. --- louloulibs/xmpp/xmpp_stanza.hpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp index 3d5b0c5..ee6b25b 100644 --- a/louloulibs/xmpp/xmpp_stanza.hpp +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -1,7 +1,7 @@ #ifndef XMPP_STANZA_INCLUDED # define XMPP_STANZA_INCLUDED -#include +#include #include #include @@ -24,18 +24,9 @@ class XmlNode public: explicit XmlNode(const std::string& name, XmlNode* parent); explicit XmlNode(const std::string& name); - XmlNode(XmlNode&& node): - name(std::move(node.name)), - parent(node.parent), - attributes(std::move(node.attributes)), - children(std::move(node.children)), - inner(std::move(node.inner)), - tail(std::move(node.tail)) - { - node.parent = nullptr; - } + XmlNode(XmlNode&& node) = default; /** - * The copy constructor do not copy the parent attribute. The children + * The copy constructor does not copy the parent attribute. The children * nodes are all copied recursively. */ XmlNode(const XmlNode& node): @@ -134,7 +125,7 @@ public: private: std::string name; XmlNode* parent; - std::unordered_map attributes; + std::map attributes; std::vector children; std::string inner; std::string tail; -- cgit v1.2.3 From f3b3d937ae274d0eec4a737d11ba19a7f4ceef03 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Sep 2015 14:47:57 +0200 Subject: =?UTF-8?q?Use=20unique=5Fptr=20to=20store=20the=20XmlNode?= =?UTF-8?q?=E2=80=99s=20children?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fix some constness things --- louloulibs/xmpp/adhoc_command.cpp | 8 ++++---- louloulibs/xmpp/xmpp_component.cpp | 6 +++--- louloulibs/xmpp/xmpp_parser.cpp | 19 ++++++++++--------- louloulibs/xmpp/xmpp_parser.hpp | 5 +++++ louloulibs/xmpp/xmpp_stanza.cpp | 36 ++++++++++++++---------------------- louloulibs/xmpp/xmpp_stanza.hpp | 18 ++++++++---------- 6 files changed, 44 insertions(+), 48 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/adhoc_command.cpp b/louloulibs/xmpp/adhoc_command.cpp index c17f1d9..b8b07c6 100644 --- a/louloulibs/xmpp/adhoc_command.cpp +++ b/louloulibs/xmpp/adhoc_command.cpp @@ -51,11 +51,11 @@ void HelloStep1(XmppComponent*, AdhocSession&, XmlNode& command_node) void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node) { // Find out if the name was provided in the form. - XmlNode* x = command_node.get_child("x", "jabber:x:data"); + const XmlNode* x = command_node.get_child("x", "jabber:x:data"); if (x) { - XmlNode* name_field = nullptr; - for (XmlNode* field: x->get_children("field", "jabber:x:data")) + const XmlNode* name_field = nullptr; + for (const XmlNode* field: x->get_children("field", "jabber:x:data")) if (field->get_tag("var") == "name") { name_field = field; @@ -63,7 +63,7 @@ void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node) } if (name_field) { - XmlNode* value = name_field->get_child("value", "jabber:x:data"); + const XmlNode* value = name_field->get_child("value", "jabber:x:data"); if (value) { XmlNode note("note"); diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index 19111ba..8cd722d 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -256,7 +256,7 @@ void XmppComponent::handle_handshake(const Stanza& stanza) void XmppComponent::handle_error(const Stanza& stanza) { - XmlNode* text = stanza.get_child("text", STREAMS_NS); + const XmlNode* text = stanza.get_child("text", STREAMS_NS); std::string error_message("Unspecified error"); if (text) error_message = text->get_inner(); @@ -291,7 +291,7 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con XmlNode html("html"); html["xmlns"] = XHTMLIM_NS; // Pass the ownership of the pointer to this xmlnode - html.add_child(std::get<1>(body).release()); + html.add_child(std::move(std::get<1>(body))); node.add_child(std::move(html)); } this->send_stanza(node); @@ -420,7 +420,7 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str XmlNode html("html"); html["xmlns"] = XHTMLIM_NS; // Pass the ownership of the pointer to this xmlnode - html.add_child(std::get<1>(xmpp_body).release()); + html.add_child(std::move(std::get<1>(xmpp_body))); message.add_child(std::move(html)); } this->send_stanza(message); diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp index 3903b73..c1fd63a 100644 --- a/louloulibs/xmpp/xmpp_parser.cpp +++ b/louloulibs/xmpp/xmpp_parser.cpp @@ -29,7 +29,8 @@ static void character_data_handler(void *user_data, const XML_Char *s, int len) XmppParser::XmppParser(): level(0), - current_node(nullptr) + current_node(nullptr), + root(nullptr) { this->init_xml_parser(); } @@ -47,8 +48,6 @@ void XmppParser::init_xml_parser() XmppParser::~XmppParser() { - if (this->current_node) - delete this->current_node; XML_ParserFree(this->parser); } @@ -75,9 +74,8 @@ void XmppParser::reset() { XML_ParserFree(this->parser); this->init_xml_parser(); - if (this->current_node) - delete this->current_node; this->current_node = nullptr; + this->root.reset(nullptr); this->level = 0; } @@ -90,10 +88,13 @@ void XmppParser::start_element(const XML_Char* name, const XML_Char** attribute) { level++; - XmlNode* new_node = new XmlNode(name, this->current_node); + auto new_node = std::make_unique(name, this->current_node); + auto new_node_ptr = new_node.get(); if (this->current_node) - this->current_node->add_child(new_node); - this->current_node = new_node; + this->current_node->add_child(std::move(new_node)); + else + this->root = std::move(new_node); + this->current_node = new_node_ptr; for (size_t i = 0; attribute[i]; i += 2) this->current_node->set_attribute(attribute[i], attribute[i+1]); if (this->level == 1) @@ -111,8 +112,8 @@ void XmppParser::end_element(const XML_Char* name) if (level == 0) { this->stream_close_event(*this->current_node); - delete this->current_node; this->current_node = nullptr; + this->root.reset(); } else this->current_node = this->current_node->get_parent(); diff --git a/louloulibs/xmpp/xmpp_parser.hpp b/louloulibs/xmpp/xmpp_parser.hpp index 79c9f8f..4de639d 100644 --- a/louloulibs/xmpp/xmpp_parser.hpp +++ b/louloulibs/xmpp/xmpp_parser.hpp @@ -111,6 +111,11 @@ private: * new children, inner or tail) */ XmlNode* current_node; + /** + * The root node has no parent, so we keep it here: the XmppParser object + * is its owner. + */ + std::unique_ptr root; /** * A list of callbacks to be called on an *_event, receiving the * concerned Stanza/XmlNode. diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp index aec91a7..3ba8483 100644 --- a/louloulibs/xmpp/xmpp_stanza.cpp +++ b/louloulibs/xmpp/xmpp_stanza.cpp @@ -103,17 +103,8 @@ XmlNode::XmlNode(const std::string& name): { } -XmlNode::~XmlNode() -{ - this->delete_all_children(); -} - void XmlNode::delete_all_children() { - for (auto& child: this->children) - { - delete child; - } this->children.clear(); } @@ -152,43 +143,44 @@ std::string XmlNode::get_tail() const return this->tail; } -XmlNode* XmlNode::get_child(const std::string& name, const std::string& xmlns) const +const XmlNode* XmlNode::get_child(const std::string& name, const std::string& xmlns) const { - for (auto& child: this->children) + for (const auto& child: this->children) { if (child->name == name && child->get_tag("xmlns") == xmlns) - return child; + return child.get(); } return nullptr; } -std::vector XmlNode::get_children(const std::string& name, const std::string& xmlns) const +std::vector XmlNode::get_children(const std::string& name, const std::string& xmlns) const { - std::vector res; - for (auto& child: this->children) + std::vector res; + for (const auto& child: this->children) { if (child->name == name && child->get_tag("xmlns") == xmlns) - res.push_back(child); + res.push_back(child.get()); } return res; } -XmlNode* XmlNode::add_child(XmlNode* child) +XmlNode* XmlNode::add_child(std::unique_ptr child) { child->parent = this; - this->children.push_back(child); - return child; + auto ret = child.get(); + this->children.push_back(std::move(child)); + return ret; } XmlNode* XmlNode::add_child(XmlNode&& child) { - XmlNode* new_node = new XmlNode(std::move(child)); - return this->add_child(new_node); + auto new_node = std::make_unique(std::move(child)); + return this->add_child(std::move(new_node)); } XmlNode* XmlNode::get_last_child() const { - return this->children.back(); + return this->children.back().get(); } XmlNode* XmlNode::get_parent() const diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp index ee6b25b..4a54379 100644 --- a/louloulibs/xmpp/xmpp_stanza.hpp +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -4,6 +4,7 @@ #include #include #include +#include std::string xml_escape(const std::string& data); std::string xml_unescape(const std::string& data); @@ -37,14 +38,11 @@ public: inner(node.inner), tail(node.tail) { - for (XmlNode* child: node.children) - { - XmlNode* child_copy = new XmlNode(*child); - this->add_child(child_copy); - } + for (const auto& child: node.children) + this->add_child(std::make_unique(*child)); } - ~XmlNode(); + ~XmlNode() = default; void delete_all_children(); void set_attribute(const std::string& name, const std::string& value); @@ -78,16 +76,16 @@ public: /** * Get a pointer to the first child element with that name and that xml namespace */ - XmlNode* get_child(const std::string& name, const std::string& xmlns) const; + const XmlNode* get_child(const std::string& name, const std::string& xmlns) const; /** * Get a vector of all the children that have that name and that xml namespace. */ - std::vector get_children(const std::string& name, const std::string& xmlns) const; + std::vector get_children(const std::string& name, const std::string& xmlns) const; /** * Add a node child to this node. Assign this node to the child’s parent. * Returns a pointer to the newly added child. */ - XmlNode* add_child(XmlNode* child); + XmlNode* add_child(std::unique_ptr child); XmlNode* add_child(XmlNode&& child); /** * Returns the last of the children. If the node doesn't have any child, @@ -126,7 +124,7 @@ private: std::string name; XmlNode* parent; std::map attributes; - std::vector children; + std::vector> children; std::string inner; std::string tail; -- cgit v1.2.3 From c3309d0acfedaed867ae1bc14cd7a65fe5be1419 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Sep 2015 20:01:44 +0200 Subject: Trivial cleanup --- louloulibs/xmpp/xmpp_component.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index 8cd722d..2214ecf 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -101,13 +101,9 @@ void XmppComponent::on_connected() void XmppComponent::on_connection_close(const std::string& error) { if (error.empty()) - { - log_info("XMPP server closed connection"); - } + log_info("XMPP server closed connection"); else - { - log_info("XMPP server closed connection: " << error); - } + log_info("XMPP server closed connection: " << error); } void XmppComponent::parse_in_buffer(const size_t size) -- cgit v1.2.3 From ea0b2f2bb871e7760d1936bb9b193655682df413 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 18 Sep 2015 09:18:33 +0200 Subject: Create a xdg_path function --- louloulibs/louloulibs.h.cmake | 3 ++- louloulibs/utils/xdg.cpp | 20 ++++++++++++++++++++ louloulibs/utils/xdg.hpp | 13 +++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 louloulibs/utils/xdg.cpp create mode 100644 louloulibs/utils/xdg.hpp (limited to 'louloulibs') diff --git a/louloulibs/louloulibs.h.cmake b/louloulibs/louloulibs.h.cmake index 707d3fe..2feaf4e 100644 --- a/louloulibs/louloulibs.h.cmake +++ b/louloulibs/louloulibs.h.cmake @@ -5,4 +5,5 @@ #cmakedefine POLLER ${POLLER} #cmakedefine BOTAN_FOUND #cmakedefine CARES_FOUND -#cmakedefine SOFTWARE_VERSION "${SOFTWARE_VERSION}" \ No newline at end of file +#cmakedefine SOFTWARE_VERSION "${SOFTWARE_VERSION}" +#cmakedefine PROJECT_NAME "${PROJECT_NAME}" \ No newline at end of file diff --git a/louloulibs/utils/xdg.cpp b/louloulibs/utils/xdg.cpp new file mode 100644 index 0000000..7a60dd8 --- /dev/null +++ b/louloulibs/utils/xdg.cpp @@ -0,0 +1,20 @@ +#include +#include + +#include "louloulibs.h" + +std::string xdg_config_path(const std::string& filename) +{ + const char* xdg_config_home = ::getenv("XDG_CONFIG_HOME"); + if (xdg_config_home && xdg_config_home[0] == '/') + return std::string{xdg_config_home} + "/" PROJECT_NAME "/" + filename; + else + { + const char* home = ::getenv("HOME"); + if (home) + return std::string{home} + "/" ".config" "/" PROJECT_NAME "/" + filename; + else + return filename; + } +} + diff --git a/louloulibs/utils/xdg.hpp b/louloulibs/utils/xdg.hpp new file mode 100644 index 0000000..20f5bb7 --- /dev/null +++ b/louloulibs/utils/xdg.hpp @@ -0,0 +1,13 @@ +#ifndef XDG_HPP_INCLUDED +#define XDG_HPP_INCLUDED + +#include + +/** + * Returns a path for the given filename, according to the XDG base + * directory specification, see + * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + */ +std::string xdg_config_path(const std::string& filename); + +#endif /* XDG_HPP_INCLUDED */ -- cgit v1.2.3 From 33fa1dcd5a87035de1d9b8df65e5c7551b4bbd1b Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 18 Sep 2015 09:39:49 +0200 Subject: Also a xdg_data_path --- louloulibs/utils/xdg.cpp | 17 +++++++++++++---- louloulibs/utils/xdg.hpp | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/utils/xdg.cpp b/louloulibs/utils/xdg.cpp index 7a60dd8..48212a1 100644 --- a/louloulibs/utils/xdg.cpp +++ b/louloulibs/utils/xdg.cpp @@ -3,11 +3,11 @@ #include "louloulibs.h" -std::string xdg_config_path(const std::string& filename) +std::string xdg_path(const std::string& filename, const char* env_var) { - const char* xdg_config_home = ::getenv("XDG_CONFIG_HOME"); - if (xdg_config_home && xdg_config_home[0] == '/') - return std::string{xdg_config_home} + "/" PROJECT_NAME "/" + filename; + const char* xdg_home = ::getenv(env_var); + if (xdg_home && xdg_home[0] == '/') + return std::string{xdg_home} + "/" PROJECT_NAME "/" + filename; else { const char* home = ::getenv("HOME"); @@ -18,3 +18,12 @@ std::string xdg_config_path(const std::string& filename) } } +std::string xdg_config_path(const std::string& filename) +{ + return xdg_path(filename, "XDG_CONFIG_HOME"); +} + +std::string xdg_data_path(const std::string& filename) +{ + return xdg_path(filename, "XDG_DATA_HOME"); +} diff --git a/louloulibs/utils/xdg.hpp b/louloulibs/utils/xdg.hpp index 20f5bb7..15f3d0b 100644 --- a/louloulibs/utils/xdg.hpp +++ b/louloulibs/utils/xdg.hpp @@ -9,5 +9,6 @@ * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html */ std::string xdg_config_path(const std::string& filename); +std::string xdg_data_path(const std::string& filename); #endif /* XDG_HPP_INCLUDED */ -- cgit v1.2.3 From 19666d2ffd3ce36b7edd4044143fe535ef85dbd9 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 18 Sep 2015 21:52:11 +0200 Subject: Store the target jid in the AdhocSession objects --- louloulibs/xmpp/adhoc_commands_handler.cpp | 4 ++-- louloulibs/xmpp/adhoc_commands_handler.hpp | 2 +- louloulibs/xmpp/adhoc_session.cpp | 6 ++++-- louloulibs/xmpp/adhoc_session.hpp | 16 +++++++++++++++- 4 files changed, 22 insertions(+), 6 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/adhoc_commands_handler.cpp b/louloulibs/xmpp/adhoc_commands_handler.cpp index 458a22c..714c440 100644 --- a/louloulibs/xmpp/adhoc_commands_handler.cpp +++ b/louloulibs/xmpp/adhoc_commands_handler.cpp @@ -20,7 +20,7 @@ std::map& AdhocCommandsHandler::get_comma return this->commands; } -XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, XmlNode command_node) +XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, const std::string& to, XmlNode command_node) { std::string action = command_node.get_tag("action"); if (action.empty()) @@ -57,7 +57,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, Xm command_node["sessionid"] = sessionid; this->sessions.emplace(std::piecewise_construct, std::forward_as_tuple(sessionid, executor_jid), - std::forward_as_tuple(command_it->second, executor_jid)); + std::forward_as_tuple(command_it->second, executor_jid, to)); TimedEventsManager::instance().add_event(TimedEvent(std::chrono::steady_clock::now() + 3600s, std::bind(&AdhocCommandsHandler::remove_session, this, sessionid, executor_jid), "adhocsession"s + sessionid + executor_jid)); diff --git a/louloulibs/xmpp/adhoc_commands_handler.hpp b/louloulibs/xmpp/adhoc_commands_handler.hpp index 1083c44..cf9ca17 100644 --- a/louloulibs/xmpp/adhoc_commands_handler.hpp +++ b/louloulibs/xmpp/adhoc_commands_handler.hpp @@ -41,7 +41,7 @@ public: * Takes a copy of the node so we can actually edit it and use * it as our return value. */ - XmlNode handle_request(const std::string& executor_jid, XmlNode command_node); + XmlNode handle_request(const std::string& executor_jid, const std::string& to, XmlNode command_node); /** * Remove the session from the list. This is done to avoid filling the * memory with waiting session (for example due to a client that starts diff --git a/louloulibs/xmpp/adhoc_session.cpp b/louloulibs/xmpp/adhoc_session.cpp index fc60bb7..bf8d292 100644 --- a/louloulibs/xmpp/adhoc_session.cpp +++ b/louloulibs/xmpp/adhoc_session.cpp @@ -3,9 +3,11 @@ #include -AdhocSession::AdhocSession(const AdhocCommand& command, const std::string& jid): +AdhocSession::AdhocSession(const AdhocCommand& command, const std::string& owner_jid, + const std::string& to_jid): command(command), - owner_jid(jid), + owner_jid(owner_jid), + to_jid(to_jid), current_step(0), terminated(false) { diff --git a/louloulibs/xmpp/adhoc_session.hpp b/louloulibs/xmpp/adhoc_session.hpp index ddfb2fe..da7913f 100644 --- a/louloulibs/xmpp/adhoc_session.hpp +++ b/louloulibs/xmpp/adhoc_session.hpp @@ -23,7 +23,8 @@ typedef std::function AdhocStep; class AdhocSession { public: - explicit AdhocSession(const AdhocCommand& command, const std::string& jid); + explicit AdhocSession(const AdhocCommand& command, const std::string& owner_jid, + const std::string& to_jid); ~AdhocSession(); /** * Return the function to be executed, found in our AdhocCommand, for the @@ -41,6 +42,15 @@ public: */ void terminate(); bool is_terminated() const; + std::string get_target_jid() const + { + return this->to_jid; + } + std::string get_owner_jid() const + { + return this->owner_jid; + } + private: /** @@ -54,6 +64,10 @@ private: * this session. */ const std::string& owner_jid; + /** + * The 'to' attribute in the request stanza. This is the target of the current session. + */ + const std::string& to_jid; /** * The current step we are at. It starts at zero. It is used to index the * associated AdhocCommand::callbacks vector. -- cgit v1.2.3 From 45e8fe56a688ec03201cdfc3dfea6ae186af682d Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 18 Sep 2015 21:55:21 +0200 Subject: Add an AdhocCommandsHandler to store commands specific to IRC servers --- louloulibs/xmpp/xmpp_component.cpp | 8 ++++---- louloulibs/xmpp/xmpp_component.hpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index 2214ecf..3017c0b 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -587,22 +587,22 @@ void XmppComponent::send_version(const std::string& id, const std::string& jid_t this->send_stanza(iq); } -void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, const bool with_admin_only) +void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, const std::string& from_jid, const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler) { Stanza iq("iq"); iq["type"] = "result"; iq["id"] = id; iq["to"] = requester_jid; - iq["from"] = this->served_hostname; + iq["from"] = from_jid; XmlNode query("query"); query["xmlns"] = DISCO_ITEMS_NS; query["node"] = ADHOC_NS; - for (const auto& kv: this->adhoc_commands_handler.get_commands()) + for (const auto& kv: adhoc_handler.get_commands()) { if (kv.second.is_admin_only() && !with_admin_only) continue; XmlNode item("item"); - item["jid"] = this->served_hostname; + item["jid"] = from_jid; item["node"] = kv.first; item["name"] = kv.second.name; query.add_child(std::move(item)); diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp index e45bb36..06236fe 100644 --- a/louloulibs/xmpp/xmpp_component.hpp +++ b/louloulibs/xmpp/xmpp_component.hpp @@ -184,8 +184,8 @@ public: * Send the list of all available ad-hoc commands to that JID. The list is * different depending on what JID made the request. */ - void send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, - const bool with_admin_only); + void send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, const std::string& from_jid, + const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler); /** * Send an iq version request */ -- cgit v1.2.3 From 1031989fe26f219ccb4b9a602a599e30f18cb9d2 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 18 Sep 2015 21:56:23 +0200 Subject: XmlNode::add_child can also take a copy of a node --- louloulibs/xmpp/xmpp_stanza.cpp | 6 ++++++ louloulibs/xmpp/xmpp_stanza.hpp | 1 + 2 files changed, 7 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp index 3ba8483..4c0f5c7 100644 --- a/louloulibs/xmpp/xmpp_stanza.cpp +++ b/louloulibs/xmpp/xmpp_stanza.cpp @@ -178,6 +178,12 @@ XmlNode* XmlNode::add_child(XmlNode&& child) return this->add_child(std::move(new_node)); } +XmlNode* XmlNode::add_child(const XmlNode& child) +{ + auto new_node = std::make_unique(child); + return this->add_child(std::move(new_node)); +} + XmlNode* XmlNode::get_last_child() const { return this->children.back().get(); diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp index 4a54379..9cf7d8d 100644 --- a/louloulibs/xmpp/xmpp_stanza.hpp +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -87,6 +87,7 @@ public: */ XmlNode* add_child(std::unique_ptr child); XmlNode* add_child(XmlNode&& child); + XmlNode* add_child(const XmlNode& child); /** * Returns the last of the children. If the node doesn't have any child, * the behaviour is undefined. The user should make sure this is the case -- cgit v1.2.3 From 1691cf8f64d6175c61ddf9a4f4a95b44c32d698e Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 18 Sep 2015 22:01:02 +0200 Subject: Introduce the configure ad-hoc command on irc servers Provides two options for now, and they have no effect yet --- louloulibs/utils/string.cpp | 6 ++++++ louloulibs/utils/string.hpp | 8 ++++++++ 2 files changed, 14 insertions(+) create mode 100644 louloulibs/utils/string.cpp create mode 100644 louloulibs/utils/string.hpp (limited to 'louloulibs') diff --git a/louloulibs/utils/string.cpp b/louloulibs/utils/string.cpp new file mode 100644 index 0000000..3977feb --- /dev/null +++ b/louloulibs/utils/string.cpp @@ -0,0 +1,6 @@ +#include + +bool to_bool(const std::string& val) +{ + return (val == "1" || val == "true"); +} diff --git a/louloulibs/utils/string.hpp b/louloulibs/utils/string.hpp new file mode 100644 index 0000000..3775c36 --- /dev/null +++ b/louloulibs/utils/string.hpp @@ -0,0 +1,8 @@ +#ifndef STRING_UTILS_HPP_INCLUDED +#define STRING_UTILS_HPP_INCLUDED + +#include + +bool to_bool(const std::string& val); + +#endif /* STRING_UTILS_HPP_INCLUDED */ -- cgit v1.2.3 From 2928598e773ca6708efdda1b6c35786cd3fa5587 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 22 Sep 2015 03:59:14 +0200 Subject: Catch TLS exceptions, close the connection and inform the user of the error --- louloulibs/network/tcp_socket_handler.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index cca6cd2..9424a1a 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -20,6 +20,7 @@ #ifdef BOTAN_FOUND # include +# include Botan::AutoSeeded_RNG TCPSocketHandler::rng; Permissive_Credentials_Manager TCPSocketHandler::credential_manager; @@ -364,7 +365,13 @@ void TCPSocketHandler::send_data(std::string&& data) { #ifdef BOTAN_FOUND if (this->use_tls) - this->tls_send(std::move(data)); + try { + this->tls_send(std::move(data)); + } catch (const Botan::TLS::TLS_Exception& e) { + this->on_connection_close("TLS error: "s + e.what()); + this->close(); + return ; + } else #endif this->raw_send(std::move(data)); @@ -426,8 +433,17 @@ void TCPSocketHandler::tls_recv() if (size > 0) { const bool was_active = this->tls->is_active(); - this->tls->received_data(reinterpret_cast(recv_buf), - static_cast(size)); + try { + this->tls->received_data(reinterpret_cast(recv_buf), + static_cast(size)); + } catch (const Botan::TLS::TLS_Exception& e) { + // May happen if the server sends malformed TLS data (buggy server, + // or more probably we are just connected to a server that sends + // plain-text) + this->on_connection_close("TLS error: "s + e.what()); + this->close(); + return ; + } if (!was_active && this->tls->is_active()) this->on_tls_activated(); } @@ -441,7 +457,7 @@ void TCPSocketHandler::tls_send(std::string&& data) if (!this->pre_buf.empty()) { this->tls->send(reinterpret_cast(this->pre_buf.data()), - this->pre_buf.size()); + this->pre_buf.size()); this->pre_buf = ""; } if (!data.empty()) -- cgit v1.2.3 From aa340e1c5e4e28397ef212aa210633e9dcb81f98 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 15 Oct 2015 04:32:02 +0200 Subject: Separate the DNS resolution logic from the TCP communication logic fix #3137 --- louloulibs/network/dns_handler.cpp | 23 +--- louloulibs/network/dns_handler.hpp | 7 +- louloulibs/network/resolver.cpp | 219 ++++++++++++++++++++++++++++++ louloulibs/network/resolver.hpp | 129 ++++++++++++++++++ louloulibs/network/tcp_socket_handler.cpp | 202 +++------------------------ louloulibs/network/tcp_socket_handler.hpp | 49 ++----- 6 files changed, 383 insertions(+), 246 deletions(-) create mode 100644 louloulibs/network/resolver.cpp create mode 100644 louloulibs/network/resolver.hpp (limited to 'louloulibs') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index 324784b..a8d0f9c 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -2,7 +2,6 @@ #ifdef CARES_FOUND #include -#include #include #include @@ -14,19 +13,6 @@ DNSHandler DNSHandler::instance; using namespace std::string_literals; - -void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent) -{ - TCPSocketHandler* socket_handler = static_cast(arg); - socket_handler->on_hostname4_resolved(status, hostent); -} - -void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent) -{ - TCPSocketHandler* socket_handler = static_cast(arg); - socket_handler->on_hostname6_resolved(status, hostent); -} - DNSHandler::DNSHandler() { int ares_error; @@ -54,16 +40,15 @@ void DNSHandler::destroy() ::ares_library_cleanup(); } -void DNSHandler::gethostbyname(const std::string& name, - TCPSocketHandler* socket_handler, int family) +void DNSHandler::gethostbyname(const std::string& name, ares_host_callback callback, + void* data, int family) { - socket_handler->free_cares_addrinfo(); if (family == AF_INET) ::ares_gethostbyname(this->channel, name.data(), family, - &::on_hostname4_resolved, socket_handler); + callback, data); else ::ares_gethostbyname(this->channel, name.data(), family, - &::on_hostname6_resolved, socket_handler); + callback, data); } void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index ec35374..61e2302 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -13,9 +13,6 @@ class DNSSocketHandler; # include # include -void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent); -void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent); - /** * Class managing DNS resolution. It should only be statically instanciated * once in SocketHandler. It manages ares channel and calls various @@ -27,8 +24,8 @@ class DNSHandler public: DNSHandler(); ~DNSHandler() = default; - void gethostbyname(const std::string& name, TCPSocketHandler* socket_handler, - int family); + void gethostbyname(const std::string& name, ares_host_callback callback, + void* socket_handler, int family); /** * Call ares_fds to know what fd needs to be watched by the poller, create * or destroy DNSSocketHandlers depending on the result. diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp new file mode 100644 index 0000000..bfd75d7 --- /dev/null +++ b/louloulibs/network/resolver.cpp @@ -0,0 +1,219 @@ +#include +#include +#include +#include + +// remove me +#include + +using namespace std::string_literals; + +Resolver::Resolver(): +#ifdef CARES_FOUND + resolved4(false), + resolved6(false), + resolving(false), + cares_addrinfo(nullptr), + port{}, +#endif + resolved(false), + error_msg{} +{ +} + +void Resolver::resolve(const std::string& hostname, const std::string& port, + SuccessCallbackType success_cb, ErrorCallbackType error_cb) +{ + this->error_cb = error_cb; + this->success_cb = success_cb; + this->port = port; + + this->start_resolving(hostname, port); +} + +#ifdef CARES_FOUND +void Resolver::start_resolving(const std::string& hostname, const std::string& port) +{ + std::cout << "start_resolving: " << hostname << port << std::endl; + this->resolving = true; + this->resolved = false; + this->resolved4 = false; + this->resolved6 = false; + + this->error_msg.clear(); + this->cares_addrinfo = nullptr; + + auto hostname4_resolved = [](void* arg, int status, int, + struct hostent* hostent) + { + Resolver* resolver = static_cast(arg); + resolver->on_hostname4_resolved(status, hostent); + }; + auto hostname6_resolved = [](void* arg, int status, int, + struct hostent* hostent) + { + Resolver* resolver = static_cast(arg); + resolver->on_hostname6_resolved(status, hostent); + }; + + DNSHandler::instance.gethostbyname(hostname, hostname6_resolved, + this, AF_INET6); + DNSHandler::instance.gethostbyname(hostname, hostname4_resolved, + this, AF_INET); +} + +void Resolver::on_hostname4_resolved(int status, struct hostent* hostent) +{ + this->resolved4 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo4(hostent); + else + this->error_msg = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + this->on_resolved(); +} + +void Resolver::on_hostname6_resolved(int status, struct hostent* hostent) +{ + this->resolved6 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo6(hostent); + else + this->error_msg = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + this->on_resolved(); +} + +void Resolver::on_resolved() +{ + this->resolved = true; + this->resolving = false; + if (!this->cares_addrinfo) + { + if (this->error_cb) + this->error_cb(this->error_msg.data()); + } + else + { + this->addr.reset(this->cares_addrinfo); + if (this->success_cb) + this->success_cb(this->addr.get()); + } +} + +void Resolver::fill_ares_addrinfo4(const struct hostent* hostent) +{ + std::cout << "fill_ares_addrinfo4" << this->port << std::endl; + struct addrinfo* prev = this->cares_addrinfo; + struct in_addr** address = reinterpret_cast(hostent->h_addr_list); + + while (*address) + { + // Create a new addrinfo list element, and fill it + struct addrinfo* current = new struct addrinfo; + current->ai_flags = 0; + current->ai_family = hostent->h_addrtype; + current->ai_socktype = SOCK_STREAM; + current->ai_protocol = 0; + current->ai_addrlen = sizeof(struct sockaddr_in); + + struct sockaddr_in* addr = new struct sockaddr_in; + addr->sin_family = hostent->h_addrtype; + addr->sin_port = htons(strtoul(this->port.data(), nullptr, 10)); + addr->sin_addr.s_addr = (*address)->s_addr; + + current->ai_addr = reinterpret_cast(addr); + current->ai_next = nullptr; + current->ai_canonname = nullptr; + + current->ai_next = prev; + this->cares_addrinfo = current; + prev = current; + ++address; + } +} + +void Resolver::fill_ares_addrinfo6(const struct hostent* hostent) +{ + std::cout << "fill_ares_addrinfo6" << this->port << std::endl; + struct addrinfo* prev = this->cares_addrinfo; + struct in6_addr** address = reinterpret_cast(hostent->h_addr_list); + + while (*address) + { + // Create a new addrinfo list element, and fill it + struct addrinfo* current = new struct addrinfo; + current->ai_flags = 0; + current->ai_family = hostent->h_addrtype; + current->ai_socktype = SOCK_STREAM; + current->ai_protocol = 0; + current->ai_addrlen = sizeof(struct sockaddr_in6); + + struct sockaddr_in6* addr = new struct sockaddr_in6; + addr->sin6_family = hostent->h_addrtype; + addr->sin6_port = htons(strtoul(this->port.data(), nullptr, 10)); + ::memcpy(addr->sin6_addr.s6_addr, (*address)->s6_addr, 16); + addr->sin6_flowinfo = 0; + addr->sin6_scope_id = 0; + + current->ai_addr = reinterpret_cast(addr); + current->ai_next = nullptr; + current->ai_canonname = nullptr; + + current->ai_next = prev; + this->cares_addrinfo = current; + prev = current; + ++address; + } +} + +#else // ifdef CARES_FOUND + +void Resolver::start_resolving(const std::string& hostname, const std::string& port) +{ + // If the resolution fails, the addr will be unset + this->addr.reset(nullptr); + + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + struct addrinfo* addr_res = nullptr; + const int res = ::getaddrinfo(hostname.data(), port.data(), + &hints, &addr_res); + + this->resolved = true; + + if (res != 0) + { + this->error_msg = gai_strerror(res); + if (this->error_cb) + this->error_cb(this->error_msg.data()); + } + else + { + this->addr.reset(addr_res); + if (this->success_cb) + this->success_cb(this->addr.get()); + } +} +#endif // ifdef CARES_FOUND + +std::string addr_to_string(const struct addrinfo* rp) +{ + char buf[INET6_ADDRSTRLEN]; + if (rp->ai_family == AF_INET) + return ::inet_ntop(rp->ai_family, + &reinterpret_cast(rp->ai_addr)->sin_addr, + buf, sizeof(buf)); + else if (rp->ai_family == AF_INET6) + return ::inet_ntop(rp->ai_family, + &reinterpret_cast(rp->ai_addr)->sin6_addr, + buf, sizeof(buf)); + return {}; +} diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp new file mode 100644 index 0000000..511afd2 --- /dev/null +++ b/louloulibs/network/resolver.hpp @@ -0,0 +1,129 @@ +#ifndef RESOLVER_HPP_INCLUDED +#define RESOLVER_HPP_INCLUDED + +#include "louloulibs.h" + +#include +#include +#include + +#include +#include +#include + +struct AddrinfoDeleter +{ + void operator()(struct addrinfo* addr) + { +#ifdef CARES_FOUND + while (addr) + { + delete addr->ai_addr; + auto next = addr->ai_next; + delete addr; + addr = next; + } +#else + freeaddrinfo(addr); +#endif + } +}; + +class Resolver +{ +public: + using ErrorCallbackType = std::function; + using SuccessCallbackType = std::function; + + Resolver(); + ~Resolver() = default; + + bool is_resolving() const + { +#ifdef CARES_FOUND + return this->resolving; +#else + return false; +#endif + } + + bool is_resolved() const + { + return this->resolved; + } + + const auto& get_result() const + { + return this->addr; + } + std::string get_error_message() const + { + return this->error_msg; + } + + void clear() + { +#ifdef CARES_FOUND + this->resolved6 = false; + this->resolved4 = false; +#endif + this->resolved = false; + this->resolving = false; + this->addr.reset(); + this->cares_addrinfo = nullptr; + this->port.clear(); + this->error_msg.clear(); + } + + void resolve(const std::string& hostname, const std::string& port, + SuccessCallbackType success_cb, ErrorCallbackType error_cb); + +private: + void start_resolving(const std::string& hostname, const std::string& port); +#ifdef CARES_FOUND + void on_hostname4_resolved(int status, struct hostent* hostent); + void on_hostname6_resolved(int status, struct hostent* hostent); + + void fill_ares_addrinfo4(const struct hostent* hostent); + void fill_ares_addrinfo6(const struct hostent* hostent); + + void on_resolved(); + + bool resolved4; + bool resolved6; + + bool resolving; + + /** + * When using c-ares to resolve the host asynchronously, we need the + * c-ares callbacks to fill a structure (a struct addrinfo, for + * compatibility with getaddrinfo and the rest of the code that works when + * c-ares is not used) with all returned values (for example an IPv6 and + * an IPv4). The pointer is given to the unique_ptr to manage its lifetime. + */ + struct addrinfo* cares_addrinfo; + std::string port; + +#endif + /** + * Tells if we finished the resolution process. It doesn't indicate if it + * was successful (it is true even if the result is an error). + */ + bool resolved; + std::string error_msg; + + + std::unique_ptr addr; + + ErrorCallbackType error_cb; + SuccessCallbackType success_cb; + + Resolver(const Resolver&) = delete; + Resolver(Resolver&&) = delete; + Resolver& operator=(const Resolver&) = delete; + Resolver& operator=(Resolver&&) = delete; +}; + +std::string addr_to_string(const struct addrinfo* rp); + +#endif /* RESOLVER_HPP_INCLUDED */ diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 9424a1a..72abdcc 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -11,13 +11,9 @@ #include #include #include -#include #include #include -#include -#include - #ifdef BOTAN_FOUND # include # include @@ -43,24 +39,9 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): use_tls(false), connected(false), connecting(false), -#ifdef CARES_FOUND - resolving(false), - resolved(false), - resolved4(false), - resolved6(false), - cares_addrinfo(nullptr), - cares_error(), -#endif hostname_resolution_failed(false) {} -TCPSocketHandler::~TCPSocketHandler() -{ -#ifdef CARES_FOUND - this->free_cares_addrinfo(); -#endif -} - void TCPSocketHandler::init_socket(const struct addrinfo* rp) { if ((this->socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) @@ -91,17 +72,24 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po { // Get the addrinfo from getaddrinfo (or ares_gethostbyname), only if // this is the first call of this function. -#ifdef CARES_FOUND - if (!this->resolved) + if (!this->resolver.is_resolved()) { log_info("Trying to connect to " << address << ":" << port); // Start the asynchronous process of resolving the hostname. Once // the addresses have been found and `resolved` has been set to true // (but connecting will still be false), TCPSocketHandler::connect() // needs to be called, again. - this->resolving = true; - DNSHandler::instance.gethostbyname(address, this, AF_INET6); - DNSHandler::instance.gethostbyname(address, this, AF_INET); + this->resolver.resolve(address, port, + [this](const struct addrinfo*) + { + log_debug("Resolution success, calling connect() again"); + this->connect(); + }, + [this](const char*) + { + log_debug("Resolution failed, calling connect() again"); + this->connect(); + }); return; } else @@ -109,39 +97,16 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po // The c-ares resolved the hostname and the available addresses // where saved in the cares_addrinfo linked list. Now, just use // this list to try to connect. - addr_res = this->cares_addrinfo; + addr_res = this->resolver.get_result().get(); if (!addr_res) { this->hostname_resolution_failed = true; - const auto msg = this->cares_error; + const auto msg = this->resolver.get_error_message(); this->close(); this->on_connection_failed(msg); return ; } } -#else - log_info("Trying to connect to " << address << ":" << port); - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_flags = 0; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - - const int res = ::getaddrinfo(address.c_str(), port.c_str(), &hints, &addr_res); - - if (res != 0) - { - log_warning("getaddrinfo failed: "s + gai_strerror(res)); - this->hostname_resolution_failed = true; - this->close(); - this->on_connection_failed(gai_strerror(res)); - return ; - } - // Make sure the alloced structure is always freed at the end of the - // function - sg.add_callback([&addr_res](){ freeaddrinfo(addr_res); }); -#endif } else { // This function is called again, use the saved addrinfo structure, @@ -335,30 +300,18 @@ void TCPSocketHandler::close() } this->connected = false; this->connecting = false; -#ifdef CARES_FOUND - this->resolving = false; - this->resolved = false; - this->resolved4 = false; - this->resolved6 = false; - this->free_cares_addrinfo(); - this->cares_error.clear(); -#endif this->in_buf.clear(); this->out_buf.clear(); this->port.clear(); + this->resolver.clear(); } void TCPSocketHandler::display_resolved_ip(struct addrinfo* rp) const { - char buf[INET6_ADDRSTRLEN]; if (rp->ai_family == AF_INET) - log_debug("Connecting to IP address " << ::inet_ntop(rp->ai_family, - &reinterpret_cast(rp->ai_addr)->sin_addr, - buf, sizeof(buf))); + log_debug("Trying IPv4 address " << addr_to_string(rp)); else if (rp->ai_family == AF_INET6) - log_debug("Connecting to IPv6 address " << ::inet_ntop(rp->ai_family, - &reinterpret_cast(rp->ai_addr)->sin6_addr, - buf, sizeof(buf))); + log_debug("Trying IPv6 address " << addr_to_string(rp)); } void TCPSocketHandler::send_data(std::string&& data) @@ -399,11 +352,7 @@ bool TCPSocketHandler::is_connected() const bool TCPSocketHandler::is_connecting() const { -#ifdef CARES_FOUND - return this->connecting || this->resolving; -#else - return this->connecting; -#endif + return this->connecting || this->resolver.is_resolving(); } void* TCPSocketHandler::get_receive_buffer(const size_t) const @@ -506,122 +455,11 @@ void TCPSocketHandler::on_tls_activated() } void Permissive_Credentials_Manager::verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector&) + const std::string& purported_hostname, + const std::vector&) { // TODO: Offer the admin to disallow connection on untrusted // certificates log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); } #endif // BOTAN_FOUND - -#ifdef CARES_FOUND - -void TCPSocketHandler::on_hostname4_resolved(int status, struct hostent* hostent) -{ - this->resolved4 = true; - if (status == ARES_SUCCESS) - this->fill_ares_addrinfo4(hostent); - else - this->cares_error = ::ares_strerror(status); - - if (this->resolved4 && this->resolved6) - { - this->resolved = true; - this->resolving = false; - this->connect(); - } -} - -void TCPSocketHandler::on_hostname6_resolved(int status, struct hostent* hostent) -{ - this->resolved6 = true; - if (status == ARES_SUCCESS) - this->fill_ares_addrinfo6(hostent); - else - this->cares_error = ::ares_strerror(status); - - if (this->resolved4 && this->resolved6) - { - this->resolved = true; - this->resolving = false; - this->connect(); - } -} - -void TCPSocketHandler::fill_ares_addrinfo4(const struct hostent* hostent) -{ - struct addrinfo* prev = this->cares_addrinfo; - struct in_addr** address = reinterpret_cast(hostent->h_addr_list); - - while (*address) - { - // Create a new addrinfo list element, and fill it - struct addrinfo* current = new struct addrinfo; - current->ai_flags = 0; - current->ai_family = hostent->h_addrtype; - current->ai_socktype = SOCK_STREAM; - current->ai_protocol = 0; - current->ai_addrlen = sizeof(struct sockaddr_in); - - struct sockaddr_in* addr = new struct sockaddr_in; - addr->sin_family = hostent->h_addrtype; - addr->sin_port = htons(strtoul(this->port.data(), nullptr, 10)); - addr->sin_addr.s_addr = (*address)->s_addr; - - current->ai_addr = reinterpret_cast(addr); - current->ai_next = nullptr; - current->ai_canonname = nullptr; - - current->ai_next = prev; - this->cares_addrinfo = current; - prev = current; - ++address; - } -} - -void TCPSocketHandler::fill_ares_addrinfo6(const struct hostent* hostent) -{ - struct addrinfo* prev = this->cares_addrinfo; - struct in6_addr** address = reinterpret_cast(hostent->h_addr_list); - - while (*address) - { - // Create a new addrinfo list element, and fill it - struct addrinfo* current = new struct addrinfo; - current->ai_flags = 0; - current->ai_family = hostent->h_addrtype; - current->ai_socktype = SOCK_STREAM; - current->ai_protocol = 0; - current->ai_addrlen = sizeof(struct sockaddr_in6); - - struct sockaddr_in6* addr = new struct sockaddr_in6; - addr->sin6_family = hostent->h_addrtype; - addr->sin6_port = htons(strtoul(this->port.data(), nullptr, 10)); - ::memcpy(addr->sin6_addr.s6_addr, (*address)->s6_addr, 16); - addr->sin6_flowinfo = 0; - addr->sin6_scope_id = 0; - - current->ai_addr = reinterpret_cast(addr); - current->ai_next = nullptr; - current->ai_canonname = nullptr; - - current->ai_next = prev; - this->cares_addrinfo = current; - prev = current; - ++address; - } -} - -void TCPSocketHandler::free_cares_addrinfo() -{ - while (this->cares_addrinfo) - { - delete this->cares_addrinfo->ai_addr; - auto next = this->cares_addrinfo->ai_next; - delete this->cares_addrinfo; - this->cares_addrinfo = next; - } -} - -#endif // CARES_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 835c509..997d575 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -2,6 +2,7 @@ # define SOCKET_HANDLER_INCLUDED #include +#include #include #include @@ -14,10 +15,6 @@ #include "louloulibs.h" -#ifdef CARES_FOUND -# include -#endif - #ifdef BOTAN_FOUND # include # include @@ -43,7 +40,7 @@ public: class TCPSocketHandler: public SocketHandler { protected: - ~TCPSocketHandler(); + ~TCPSocketHandler() = default; public: explicit TCPSocketHandler(std::shared_ptr poller); @@ -109,16 +106,6 @@ public: bool is_connected() const override final; bool is_connecting() const; -#ifdef CARES_FOUND - void on_hostname4_resolved(int status, struct hostent* hostent); - void on_hostname6_resolved(int status, struct hostent* hostent); - - void free_cares_addrinfo(); - - void fill_ares_addrinfo4(const struct hostent* hostent); - void fill_ares_addrinfo6(const struct hostent* hostent); -#endif - private: /** * Initialize the socket with the parameters contained in the given @@ -194,9 +181,13 @@ private: */ std::list out_buf; /** - * Keep the details of the addrinfo that triggered a EINPROGRESS error when - * connect()ing to it, to reuse it directly when connect() is called - * again. + * DNS resolver + */ + Resolver resolver; + /** + * Keep the details of the addrinfo returned by the resolver that + * triggered a EINPROGRESS error when connect()ing to it, to reuse it + * directly when connect() is called again. */ struct addrinfo addrinfo; struct sockaddr_in6 ai_addr; @@ -234,28 +225,6 @@ protected: bool connected; bool connecting; -#ifdef CARES_FOUND - bool resolving; - /** - * Whether or not the DNS resolution was successfully done - */ - bool resolved; - bool resolved4; - bool resolved6; - /** - * When using c-ares to resolve the host asynchronously, we need the - * c-ares callback to fill a structure (a struct addrinfo, for - * compatibility with getaddrinfo and the rest of the code that works when - * c-ares is not used) with all returned values (for example an IPv6 and - * an IPv4). The next call of connect() will then try all these values - * (exactly like we do with the result of getaddrinfo) and save the one - * that worked (or returned EINPROGRESS) in the other struct addrinfo (see - * the members addrinfo, ai_addrlen, and ai_addr). - */ - struct addrinfo* cares_addrinfo; - std::string cares_error; -#endif // CARES_FOUND - bool hostname_resolution_failed; private: -- cgit v1.2.3 From ec53fc931533a66872c9dd49b8e11aa084caf75f Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 02:34:55 +0100 Subject: Remove some debug thing that should not have been committed --- louloulibs/network/resolver.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index bfd75d7..5324191 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -3,9 +3,6 @@ #include #include -// remove me -#include - using namespace std::string_literals; Resolver::Resolver(): @@ -34,7 +31,6 @@ void Resolver::resolve(const std::string& hostname, const std::string& port, #ifdef CARES_FOUND void Resolver::start_resolving(const std::string& hostname, const std::string& port) { - std::cout << "start_resolving: " << hostname << port << std::endl; this->resolving = true; this->resolved = false; this->resolved4 = false; -- cgit v1.2.3 From f57a43f33767082e574d6cb74480d09c80e72e64 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 02:43:33 +0100 Subject: Remove some more debug thing --- louloulibs/network/resolver.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 5324191..8028c5a 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -101,7 +101,6 @@ void Resolver::on_resolved() void Resolver::fill_ares_addrinfo4(const struct hostent* hostent) { - std::cout << "fill_ares_addrinfo4" << this->port << std::endl; struct addrinfo* prev = this->cares_addrinfo; struct in_addr** address = reinterpret_cast(hostent->h_addr_list); @@ -133,7 +132,6 @@ void Resolver::fill_ares_addrinfo4(const struct hostent* hostent) void Resolver::fill_ares_addrinfo6(const struct hostent* hostent) { - std::cout << "fill_ares_addrinfo6" << this->port << std::endl; struct addrinfo* prev = this->cares_addrinfo; struct in6_addr** address = reinterpret_cast(hostent->h_addr_list); -- cgit v1.2.3 From c8606e1858363f0f9b8f9cd8ac8b96dfe181fd31 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 02:45:20 +0100 Subject: Remove a unused_parameter warning --- louloulibs/network/resolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 8028c5a..0150fc8 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -29,7 +29,7 @@ void Resolver::resolve(const std::string& hostname, const std::string& port, } #ifdef CARES_FOUND -void Resolver::start_resolving(const std::string& hostname, const std::string& port) +void Resolver::start_resolving(const std::string& hostname, const std::string&) { this->resolving = true; this->resolved = false; -- cgit v1.2.3 From 0fcba76c793e7c0b1ccc09eaa85016caa11ca3f2 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 19:44:31 +0100 Subject: Fix a build issue when compiling without cares --- louloulibs/network/resolver.cpp | 2 ++ louloulibs/network/resolver.hpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 0150fc8..cb6c445 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -23,7 +23,9 @@ void Resolver::resolve(const std::string& hostname, const std::string& port, { this->error_cb = error_cb; this->success_cb = success_cb; +#ifdef CARES_FOUND this->port = port; +#endif this->start_resolving(hostname, port); } diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index 511afd2..f9e9134 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -66,12 +66,12 @@ public: #ifdef CARES_FOUND this->resolved6 = false; this->resolved4 = false; -#endif - this->resolved = false; this->resolving = false; - this->addr.reset(); this->cares_addrinfo = nullptr; this->port.clear(); +#endif + this->resolved = false; + this->addr.reset(); this->error_msg.clear(); } -- cgit v1.2.3 From 142516a69bb000ce80cbb6509d1f407438a94663 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 20:09:39 +0100 Subject: Fix some trivial issues reported by cppcheck --- louloulibs/network/tcp_socket_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 72abdcc..f2a2466 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -247,7 +247,7 @@ void TCPSocketHandler::on_send() struct msghdr msg{nullptr, 0, msg_iov, 0, nullptr, 0, 0}; - for (std::string& s: this->out_buf) + for (const std::string& s: this->out_buf) { // unconsting the content of s is ok, sendmsg will never modify it msg_iov[msg.msg_iovlen].iov_base = const_cast(s.data()); -- cgit v1.2.3 From 450de4c33615fc8cd14bf3a7854ee615ed53df1c Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 20:28:36 +0100 Subject: =?UTF-8?q?Style,=20add=20a=20few=20=E2=80=9Cthis->=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- louloulibs/xmpp/xmpp_parser.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp index c1fd63a..f44d49a 100644 --- a/louloulibs/xmpp/xmpp_parser.cpp +++ b/louloulibs/xmpp/xmpp_parser.cpp @@ -86,7 +86,7 @@ void* XmppParser::get_buffer(const size_t size) const void XmppParser::start_element(const XML_Char* name, const XML_Char** attribute) { - level++; + this->level++; auto new_node = std::make_unique(name, this->current_node); auto new_node_ptr = new_node.get(); @@ -101,15 +101,14 @@ void XmppParser::start_element(const XML_Char* name, const XML_Char** attribute) this->stream_open_event(*this->current_node); } -void XmppParser::end_element(const XML_Char* name) +void XmppParser::end_element(const XML_Char*) { - (void)name; - level--; - if (level == 1) + this->level--; + if (this->level == 1) { this->stanza_event(*this->current_node); } - if (level == 0) + if (this->level == 0) { this->stream_close_event(*this->current_node); this->current_node = nullptr; @@ -117,7 +116,7 @@ void XmppParser::end_element(const XML_Char* name) } else this->current_node = this->current_node->get_parent(); - if (level == 1) + if (this->level == 1) this->current_node->delete_all_children(); } -- cgit v1.2.3 From 4e32fe213cccdc6cdc1dcba498fd74b8b97703ea Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 26 Oct 2015 20:35:10 +0100 Subject: Refactor XmppParser::end_element to make it clearer Both for me, and apparently for clang static analyzer, who reported a (imo) false positive. --- louloulibs/xmpp/xmpp_parser.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp index f44d49a..25f2876 100644 --- a/louloulibs/xmpp/xmpp_parser.cpp +++ b/louloulibs/xmpp/xmpp_parser.cpp @@ -104,20 +104,24 @@ void XmppParser::start_element(const XML_Char* name, const XML_Char** attribute) void XmppParser::end_element(const XML_Char*) { this->level--; - if (this->level == 1) - { - this->stanza_event(*this->current_node); - } if (this->level == 0) - { + { // End of the whole stream this->stream_close_event(*this->current_node); this->current_node = nullptr; this->root.reset(); } else - this->current_node = this->current_node->get_parent(); - if (this->level == 1) - this->current_node->delete_all_children(); + { + auto parent = this->current_node->get_parent(); + if (this->level == 1) + { // End of a stanza + this->stanza_event(*this->current_node); + // Note: deleting all the children of our parent deletes ourself, + // so current_node is an invalid pointer after this line + this->current_node->get_parent()->delete_all_children(); + } + this->current_node = parent; + } } void XmppParser::char_data(const XML_Char* data, int len) -- cgit v1.2.3 From ccd185d3f59b545e018bf3c50280e401a3715ab4 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 31 Oct 2015 06:18:29 +0100 Subject: Remove a useless assignment --- louloulibs/network/resolver.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index cb6c445..9d6de23 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -155,7 +155,6 @@ void Resolver::fill_ares_addrinfo6(const struct hostent* hostent) addr->sin6_scope_id = 0; current->ai_addr = reinterpret_cast(addr); - current->ai_next = nullptr; current->ai_canonname = nullptr; current->ai_next = prev; -- cgit v1.2.3 From f928f7627247ceaafcf3538066ac17609b652aac Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Nov 2015 03:26:13 +0100 Subject: Verify the remote TLS certificates using the system-wide trusted CAs --- louloulibs/network/credentials_manager.cpp | 33 ++++++++++++++++++++++++++++++ louloulibs/network/credentials_manager.hpp | 22 ++++++++++++++++++++ louloulibs/network/tcp_socket_handler.cpp | 12 ++--------- louloulibs/network/tcp_socket_handler.hpp | 24 ++++++---------------- 4 files changed, 63 insertions(+), 28 deletions(-) create mode 100644 louloulibs/network/credentials_manager.cpp create mode 100644 louloulibs/network/credentials_manager.hpp (limited to 'louloulibs') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp new file mode 100644 index 0000000..77198a4 --- /dev/null +++ b/louloulibs/network/credentials_manager.cpp @@ -0,0 +1,33 @@ +#include +#include + +Basic_Credentials_Manager::Basic_Credentials_Manager(): + Botan::Credentials_Manager() +{ + this->load_certs(); +} +void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector& certs) +{ + log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); + Botan::Credentials_Manager::verify_certificate_chain(type, "louiz.org", certs); + log_debug("Certificate is valid"); +} +void Basic_Credentials_Manager::load_certs() +{ + const std::vector paths = {"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"}; + for (const auto& path: paths) + { + Botan::DataSource_Stream bundle(path); + while (!bundle.end_of_data() && bundle.check_available(27)) + { + const Botan::X509_Certificate cert(bundle); + this->certificate_store.add_certificate(cert); + } + } +} +std::vector Basic_Credentials_Manager::trusted_certificate_authorities(const std::string&, const std::string&) +{ + return {&this->certificate_store}; +} diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp new file mode 100644 index 0000000..ea89eca --- /dev/null +++ b/louloulibs/network/credentials_manager.hpp @@ -0,0 +1,22 @@ +#ifndef BIBOUMI_CREDENTIALS_MANAGER_HPP +#define BIBOUMI_CREDENTIALS_MANAGER_HPP + +#include +#include + +class Basic_Credentials_Manager: public Botan::Credentials_Manager +{ +public: + Basic_Credentials_Manager(); + void verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector&) override final; + std::vector trusted_certificate_authorities(const std::string& type, + const std::string& context) override final; + +private: + void load_certs(); + Botan::Certificate_Store_In_Memory certificate_store; +}; + +#endif //BIBOUMI_CREDENTIALS_MANAGER_HPP diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index f2a2466..81a36ef 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -19,7 +19,7 @@ # include Botan::AutoSeeded_RNG TCPSocketHandler::rng; -Permissive_Credentials_Manager TCPSocketHandler::credential_manager; +Basic_Credentials_Manager TCPSocketHandler::credential_manager; Botan::TLS::Policy TCPSocketHandler::policy; Botan::TLS::Session_Manager_In_Memory TCPSocketHandler::session_manager(TCPSocketHandler::rng); @@ -451,15 +451,7 @@ bool TCPSocketHandler::tls_handshake_cb(const Botan::TLS::Session& session) void TCPSocketHandler::on_tls_activated() { - this->send_data(""); -} - -void Permissive_Credentials_Manager::verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector&) -{ // TODO: Offer the admin to disallow connection on untrusted - // certificates - log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); + this->send_data({}); } #endif // BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 997d575..d173c1f 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -1,9 +1,13 @@ #ifndef SOCKET_HANDLER_INCLUDED # define SOCKET_HANDLER_INCLUDED +#include "louloulibs.h" + #include #include +#include + #include #include #include @@ -13,23 +17,6 @@ #include #include -#include "louloulibs.h" - -#ifdef BOTAN_FOUND -# include -# include - -/** - * A very simple credential manager that accepts any certificate. - */ -class Permissive_Credentials_Manager: public Botan::Credentials_Manager -{ -public: - void verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector&); -}; -#endif // BOTAN_FOUND /** * An interface, with a series of callbacks that should be implemented in @@ -243,7 +230,7 @@ private: * Botan stuff to manipulate a TLS session. */ static Botan::AutoSeeded_RNG rng; - static Permissive_Credentials_Manager credential_manager; + static Basic_Credentials_Manager credential_manager; static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; /** @@ -267,3 +254,4 @@ private: }; #endif // SOCKET_HANDLER_INCLUDED + -- cgit v1.2.3 From 4a51850173e86f3682e9220704c9b17c09091222 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Nov 2015 17:15:32 +0100 Subject: Verify TLS certificate with the given hostname, instead of a hardcoded one --- louloulibs/network/credentials_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 77198a4..8608f90 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -11,7 +11,7 @@ void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type const std::vector& certs) { log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); - Botan::Credentials_Manager::verify_certificate_chain(type, "louiz.org", certs); + Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); log_debug("Certificate is valid"); } void Basic_Credentials_Manager::load_certs() -- cgit v1.2.3 From 06db9b366a83121e0c914e527a367f90ec71940a Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 2 Nov 2015 17:31:35 +0100 Subject: Fix the build without botan, caused by credentials_manager --- louloulibs/network/credentials_manager.cpp | 6 ++++++ louloulibs/network/credentials_manager.hpp | 5 +++++ 2 files changed, 11 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 8608f90..7c13319 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -1,5 +1,9 @@ +#include "louloulibs.h" + +#ifdef BOTAN_FOUND #include #include +#include Basic_Credentials_Manager::Basic_Credentials_Manager(): Botan::Credentials_Manager() @@ -31,3 +35,5 @@ std::vector Basic_Credentials_Manager::trusted_certif { return {&this->certificate_store}; } + +#endif diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index ea89eca..8641f1d 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -1,6 +1,10 @@ #ifndef BIBOUMI_CREDENTIALS_MANAGER_HPP #define BIBOUMI_CREDENTIALS_MANAGER_HPP +#include "louloulibs.h" + +#ifdef BOTAN_FOUND + #include #include @@ -19,4 +23,5 @@ private: Botan::Certificate_Store_In_Memory certificate_store; }; +#endif //BOTAN_FOUND #endif //BIBOUMI_CREDENTIALS_MANAGER_HPP -- cgit v1.2.3 From e8386bd14e9783f0bef39bdf577545522e33e719 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 3 Nov 2015 16:56:38 +0100 Subject: Provide an adhoc option to let user pass the cert verif for some IRC servers --- louloulibs/network/credentials_manager.cpp | 35 +++++++++++++++++++++++++----- louloulibs/network/credentials_manager.hpp | 11 +++++++--- louloulibs/network/tcp_socket_handler.cpp | 6 +++-- louloulibs/network/tcp_socket_handler.hpp | 12 +++++++++- 4 files changed, 53 insertions(+), 11 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 7c13319..b9a9af8 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -1,25 +1,48 @@ #include "louloulibs.h" #ifdef BOTAN_FOUND +#include #include #include #include -Basic_Credentials_Manager::Basic_Credentials_Manager(): - Botan::Credentials_Manager() +#ifdef USE_DATABASE +# include +#endif + +Botan::Certificate_Store_In_Memory Basic_Credentials_Manager::certificate_store; +bool Basic_Credentials_Manager::certs_loaded = false; + +Basic_Credentials_Manager::Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler): + Botan::Credentials_Manager(), + socket_handler(socket_handler) { this->load_certs(); } + void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector& certs) { log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); - Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); - log_debug("Certificate is valid"); + try + { + Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); + log_debug("Certificate is valid"); + } + catch (const std::exception& tls_exception) + { + log_warning("TLS certificate check failed: " << tls_exception.what()); + if (this->socket_handler->abort_on_invalid_cert()) + throw; + } } + void Basic_Credentials_Manager::load_certs() { + // Only load the certificates the first time + if (Basic_Credentials_Manager::certs_loaded) + return; const std::vector paths = {"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"}; for (const auto& path: paths) { @@ -27,10 +50,12 @@ void Basic_Credentials_Manager::load_certs() while (!bundle.end_of_data() && bundle.check_available(27)) { const Botan::X509_Certificate cert(bundle); - this->certificate_store.add_certificate(cert); + Basic_Credentials_Manager::certificate_store.add_certificate(cert); } } + Basic_Credentials_Manager::certs_loaded = true; } + std::vector Basic_Credentials_Manager::trusted_certificate_authorities(const std::string&, const std::string&) { return {&this->certificate_store}; diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index 8641f1d..e292321 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -8,10 +8,12 @@ #include #include +class TCPSocketHandler; + class Basic_Credentials_Manager: public Botan::Credentials_Manager { public: - Basic_Credentials_Manager(); + Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler); void verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector&) override final; @@ -19,8 +21,11 @@ public: const std::string& context) override final; private: - void load_certs(); - Botan::Certificate_Store_In_Memory certificate_store; + const TCPSocketHandler* const socket_handler; + + static void load_certs(); + static Botan::Certificate_Store_In_Memory certificate_store; + static bool certs_loaded; }; #endif //BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 81a36ef..0ed74a2 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -19,7 +19,6 @@ # include Botan::AutoSeeded_RNG TCPSocketHandler::rng; -Basic_Credentials_Manager TCPSocketHandler::credential_manager; Botan::TLS::Policy TCPSocketHandler::policy; Botan::TLS::Session_Manager_In_Memory TCPSocketHandler::session_manager(TCPSocketHandler::rng); @@ -40,6 +39,9 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): connected(false), connecting(false), hostname_resolution_failed(false) +#ifdef BOTAN_FOUND + ,credential_manager(this) +#endif {} void TCPSocketHandler::init_socket(const struct addrinfo* rp) @@ -369,7 +371,7 @@ void TCPSocketHandler::start_tls() std::bind(&TCPSocketHandler::tls_data_cb, this, ph::_1, ph::_2), std::bind(&TCPSocketHandler::tls_alert_cb, this, ph::_1, ph::_2, ph::_3), std::bind(&TCPSocketHandler::tls_handshake_cb, this, ph::_1), - session_manager, credential_manager, policy, + session_manager, this->credential_manager, policy, rng, server_info, Botan::TLS::Protocol_Version::latest_tls_version()); } diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index d173c1f..213e286 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -90,6 +90,16 @@ public: * The size argument is the size of the last chunk of data that was added to the buffer. */ virtual void parse_in_buffer(const size_t size) = 0; +#ifdef BOTAN_FOUND + /** + * Tell whether the credential manager should cancel the connection when the + * certificate is invalid. + */ + virtual bool abort_on_invalid_cert() const + { + return true; + } +#endif bool is_connected() const override final; bool is_connecting() const; @@ -230,9 +240,9 @@ private: * Botan stuff to manipulate a TLS session. */ static Botan::AutoSeeded_RNG rng; - static Basic_Credentials_Manager credential_manager; static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; + Basic_Credentials_Manager credential_manager; /** * We use a unique_ptr because we may not want to create the object at * all. The Botan::TLS::Client object generates a handshake message and -- cgit v1.2.3 From 5ce9d3f1429228746fcee724a44860f16ad166f5 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 5 Nov 2015 02:16:21 +0100 Subject: Make the CA file configurable --- louloulibs/network/credentials_manager.cpp | 39 ++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index b9a9af8..57100ee 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -5,11 +5,22 @@ #include #include #include +#include #ifdef USE_DATABASE # include #endif +/** + * TODO find a standard way to find that out. + */ +static const std::vector default_cert_files = { + "/etc/ssl/certs/ca-bundle.crt", + "/etc/pki/tls/certs/ca-bundle.crt", + "/etc/ssl/certs/ca-certificates.crt", + "/etc/ca-certificates/extracted/tls-ca-bundle.pem" +}; + Botan::Certificate_Store_In_Memory Basic_Credentials_Manager::certificate_store; bool Basic_Credentials_Manager::certs_loaded = false; @@ -43,16 +54,34 @@ void Basic_Credentials_Manager::load_certs() // Only load the certificates the first time if (Basic_Credentials_Manager::certs_loaded) return; - const std::vector paths = {"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"}; + const std::string conf_path = Config::get("ca_file", ""); + std::vector paths; + if (conf_path.empty()) + paths = default_cert_files; + else + paths.push_back(conf_path); for (const auto& path: paths) { - Botan::DataSource_Stream bundle(path); - while (!bundle.end_of_data() && bundle.check_available(27)) + try + { + Botan::DataSource_Stream bundle(path); + log_debug("Using ca bundle: " << path); + while (!bundle.end_of_data() && bundle.check_available(27)) + { + const Botan::X509_Certificate cert(bundle); + Basic_Credentials_Manager::certificate_store.add_certificate(cert); + } + // Only use the first file that can successfully be read. + goto success; + } + catch (Botan::Stream_IO_Error& e) { - const Botan::X509_Certificate cert(bundle); - Basic_Credentials_Manager::certificate_store.add_certificate(cert); + log_debug(e.what()); } } + // If we could not open one of the files, print a warning + log_warning("The CA could not be loaded, TLS negociation will probably fail."); + success: Basic_Credentials_Manager::certs_loaded = true; } -- cgit v1.2.3 From 0620c8e589c713fa0f0d0f66a4c4d203e8f3f87f Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 7 Nov 2015 23:41:06 +0100 Subject: Avoid leaking socket filedescriptors When trying the various results of getaddrinfo, we forgot to close the socket when one fails, before trying the next one. Also use the destructor to make sure we do not have some other unrelated leak. --- louloulibs/network/tcp_socket_handler.cpp | 8 ++++++++ louloulibs/network/tcp_socket_handler.hpp | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 0ed74a2..d5c0dfa 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -44,8 +44,16 @@ TCPSocketHandler::TCPSocketHandler(std::shared_ptr poller): #endif {} +TCPSocketHandler::~TCPSocketHandler() +{ + this->close(); +} + + void TCPSocketHandler::init_socket(const struct addrinfo* rp) { + if (this->socket != -1) + ::close(this->socket); if ((this->socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) throw std::runtime_error("Could not create socket: "s + strerror(errno)); int optval = 1; diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 213e286..d33b919 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -27,7 +27,7 @@ class TCPSocketHandler: public SocketHandler { protected: - ~TCPSocketHandler() = default; + ~TCPSocketHandler(); public: explicit TCPSocketHandler(std::shared_ptr poller); -- cgit v1.2.3 From 296a16fa75da5900395a7cf70f63a6ff266ef0cc Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 30 Nov 2015 03:20:51 +0100 Subject: Do not segfault when trying to send TLS data over a not-yet connected socket --- louloulibs/network/tcp_socket_handler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index d5c0dfa..78efdce 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -410,7 +410,9 @@ void TCPSocketHandler::tls_recv() void TCPSocketHandler::tls_send(std::string&& data) { - if (this->tls->is_active()) + // We may not be connected yet, or the tls session has + // not yet been negociated + if (this->tls && this->tls->is_active()) { const bool was_active = this->tls->is_active(); if (!this->pre_buf.empty()) -- cgit v1.2.3 From 241768836ddfb9e3609f987224cd821058fcc948 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Dec 2015 16:08:40 +0100 Subject: Add the outgoing_bind option Lets the admin choose a local address to bind each outgoing (IRC) socket. --- louloulibs/network/tcp_socket_handler.cpp | 28 ++++++++++++++++++++++++++++ louloulibs/network/tcp_socket_handler.hpp | 6 ++++++ 2 files changed, 34 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 78efdce..e6901c8 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -56,6 +56,34 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) ::close(this->socket); if ((this->socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) throw std::runtime_error("Could not create socket: "s + strerror(errno)); + // Bind the socket to a specific address, if specified + if (!this->bind_addr.empty()) + { + // Convert the address from string format to a sockaddr that can be + // used in bind() + struct addrinfo* result; + int err = ::getaddrinfo(this->bind_addr.data(), nullptr, nullptr, &result); + if (err != 0) + log_error("Failed to bind socket to " << this->bind_addr << ": " + << gai_strerror(err)); + else + { + struct addrinfo* rp; + int bind_error; + for (rp = result; rp; rp = rp->ai_next) + { + if ((bind_error = ::bind(this->socket, + reinterpret_cast(rp->ai_addr), + rp->ai_addrlen)) == 0) + break; + } + if (!rp) + log_error("Failed to bind socket to " << this->bind_addr << ": " + << strerror(bind_error)); + else + log_info("Socket successfully bound to " << this->bind_addr); + } + } int optval = 1; if (::setsockopt(this->socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) log_warning("Failed to enable TCP keepalive on socket: " << strerror(errno)); diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index d33b919..9f5caa3 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -224,6 +224,12 @@ protected: bool hostname_resolution_failed; + /** + * Address to bind the socket to, before calling connect(). + * If empty, it’s equivalent to binding to INADDR_ANY. + */ + std::string bind_addr; + private: TCPSocketHandler(const TCPSocketHandler&) = delete; TCPSocketHandler(TCPSocketHandler&&) = delete; -- cgit v1.2.3 From 05a82b810f09d5cf3a0388fc4eeb523d33662c9c Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 1 Dec 2015 16:09:21 +0100 Subject: Display a better error when connection fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error should not tell “while reading” when we are just connecting. --- louloulibs/network/tcp_socket_handler.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index e6901c8..83863b0 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -266,7 +266,10 @@ ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size) } else if (-1 == size) { - log_warning("Error while reading from socket: " << strerror(errno)); + if (this->connecting) + log_warning("Error connecting: " << strerror(errno)); + else + log_warning("Error while reading from socket: " << strerror(errno)); // Remember if we were connecting, or already connected when this // happened, because close() sets this->connecting to false const auto were_connecting = this->connecting; -- cgit v1.2.3 From 6a7cd5eecfa4df0552969e5d5d57452d290b7fc5 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 3 Dec 2015 20:41:48 +0100 Subject: Do not forget to call freeaddrinfo, fix a memleak For the getaddrinfo call we added in the previous commit, to convert the IP provided in the conf. --- louloulibs/network/tcp_socket_handler.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 83863b0..6ed981c 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -68,6 +68,7 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) << gai_strerror(err)); else { + utils::ScopeGuard sg([result](){ freeaddrinfo(result); }); struct addrinfo* rp; int bind_error; for (rp = result; rp; rp = rp->ai_next) -- cgit v1.2.3 From 284af79128c4bae9fa76f2dd5916ebfd81ecf718 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 3 Dec 2015 21:08:04 +0100 Subject: =?UTF-8?q?Provide=20a=20=E2=80=9Cvar=E2=80=9D=20map=20in=20AdhocS?= =?UTF-8?q?ession=20objects,=20to=20save=20values=20between=20each=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- louloulibs/xmpp/adhoc_session.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/adhoc_session.hpp b/louloulibs/xmpp/adhoc_session.hpp index da7913f..05afc91 100644 --- a/louloulibs/xmpp/adhoc_session.hpp +++ b/louloulibs/xmpp/adhoc_session.hpp @@ -5,6 +5,7 @@ #include #include +#include class XmppComponent; @@ -75,6 +76,15 @@ private: size_t current_step; bool terminated; +public: + /** + * A map to store various things that we may want to remember between two + * steps of the same session. A step can insert any value associated to + * any key in there. + */ + std::map vars; + +private: AdhocSession(const AdhocSession&) = delete; AdhocSession(AdhocSession&&) = delete; AdhocSession& operator=(const AdhocSession&) = delete; -- cgit v1.2.3 From 756b297045c5ec3ce051b48379a692d80398ef47 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 3 Dec 2015 21:09:03 +0100 Subject: Small cleanup --- louloulibs/xmpp/adhoc_session.cpp | 4 ---- louloulibs/xmpp/adhoc_session.hpp | 7 ++----- louloulibs/xmpp/jid.cpp | 2 -- 3 files changed, 2 insertions(+), 11 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/adhoc_session.cpp b/louloulibs/xmpp/adhoc_session.cpp index bf8d292..dda4bea 100644 --- a/louloulibs/xmpp/adhoc_session.cpp +++ b/louloulibs/xmpp/adhoc_session.cpp @@ -13,10 +13,6 @@ AdhocSession::AdhocSession(const AdhocCommand& command, const std::string& owner { } -AdhocSession::~AdhocSession() -{ -} - const AdhocStep& AdhocSession::get_next_step() { assert(this->current_step < this->command.callbacks.size()); diff --git a/louloulibs/xmpp/adhoc_session.hpp b/louloulibs/xmpp/adhoc_session.hpp index 05afc91..7f07dc1 100644 --- a/louloulibs/xmpp/adhoc_session.hpp +++ b/louloulibs/xmpp/adhoc_session.hpp @@ -16,17 +16,15 @@ class AdhocSession; * A function executed as an ad-hoc command step. It takes a * XmlNode and modifies it accordingly (inserting for example an * node, or a data form…). - * TODO fix this: - * It also must call one of step_passed(), cancel() etc on the AdhocSession object. */ -typedef std::function AdhocStep; +using AdhocStep = std::function; class AdhocSession { public: explicit AdhocSession(const AdhocCommand& command, const std::string& owner_jid, const std::string& to_jid); - ~AdhocSession(); + ~AdhocSession() = default; /** * Return the function to be executed, found in our AdhocCommand, for the * current_step. And increment the current_step. @@ -52,7 +50,6 @@ public: return this->owner_jid; } - private: /** * A reference of the command concerned by this session. Used for example diff --git a/louloulibs/xmpp/jid.cpp b/louloulibs/xmpp/jid.cpp index e6fee45..dcd7012 100644 --- a/louloulibs/xmpp/jid.cpp +++ b/louloulibs/xmpp/jid.cpp @@ -30,8 +30,6 @@ Jid::Jid(const std::string& jid) this->domain = jid.substr(at, slash - at); } -#include - static constexpr size_t max_jid_part_len = 1023; std::string jidprep(const std::string& original) -- cgit v1.2.3 From 700a6c7bc39aa48af5037ecbb9778b20fb6f87df Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 3 Dec 2015 21:09:26 +0100 Subject: JID class provides bare() and full() methods --- louloulibs/xmpp/jid.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/jid.hpp b/louloulibs/xmpp/jid.hpp index b6975a2..e2ee586 100644 --- a/louloulibs/xmpp/jid.hpp +++ b/louloulibs/xmpp/jid.hpp @@ -15,6 +15,15 @@ public: std::string local; std::string resource; + std::string bare() const + { + return this->local + "@" + this->domain; + } + std::string full() const + { + return this->local + "@" + this->domain + "/" + this->resource; + } + private: Jid(const Jid&) = delete; Jid(Jid&&) = delete; -- cgit v1.2.3 From 1f6eea62f46789c0d3ebe7da133ccad2e59c89c8 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 3 Dec 2015 21:14:24 +0100 Subject: Add an ad-hoc command to disconnect a user from one or more IRC server fix #3077 --- louloulibs/xmpp/xmpp_stanza.cpp | 5 +++++ louloulibs/xmpp/xmpp_stanza.hpp | 2 ++ 2 files changed, 7 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp index 4c0f5c7..d1c2e0f 100644 --- a/louloulibs/xmpp/xmpp_stanza.cpp +++ b/louloulibs/xmpp/xmpp_stanza.cpp @@ -260,3 +260,8 @@ std::string sanitize(const std::string& data) else return xml_escape(utils::remove_invalid_xml_chars(utils::convert_to_utf8(data, "ISO-8859-1"))); } + +std::ostream& operator<<(std::ostream& os, const XmlNode& node) +{ + return os << node.to_string(); +} diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp index 9cf7d8d..b1ba54a 100644 --- a/louloulibs/xmpp/xmpp_stanza.hpp +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -140,4 +140,6 @@ private: */ typedef XmlNode Stanza; +std::ostream& operator<<(std::ostream& os, const XmlNode& node); + #endif // XMPP_STANZA_INCLUDED -- cgit v1.2.3 From 8ddbe8d3e6a5a5001537379aa4f1a418c6cb6d23 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Fri, 11 Dec 2015 15:19:40 +0000 Subject: Make the XMPP server address configurable. fix #3145 --- louloulibs/xmpp/xmpp_component.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index 3017c0b..ce9552f 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -61,7 +61,7 @@ XmppComponent::XmppComponent(std::shared_ptr poller, const std::string& void XmppComponent::start() { - this->connect("127.0.0.1", Config::get("port", "5347"), false); + this->connect(Config::get("xmpp_server_ip", "127.0.0.1"), Config::get("port", "5347"), false); } bool XmppComponent::is_document_open() const -- cgit v1.2.3 From 9714d02018904b0c207a3f4d7de0be1a6a22774b Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 21 Dec 2015 21:15:03 +0100 Subject: Also store a reference instead of a pointer, in AdhocCommandsHandler --- louloulibs/xmpp/adhoc_command.cpp | 14 ++++++-------- louloulibs/xmpp/adhoc_command.hpp | 8 ++++---- louloulibs/xmpp/adhoc_commands_handler.hpp | 7 +++---- louloulibs/xmpp/adhoc_session.hpp | 2 +- louloulibs/xmpp/xmpp_component.cpp | 2 +- 5 files changed, 15 insertions(+), 18 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/adhoc_command.cpp b/louloulibs/xmpp/adhoc_command.cpp index b8b07c6..8a8bcff 100644 --- a/louloulibs/xmpp/adhoc_command.cpp +++ b/louloulibs/xmpp/adhoc_command.cpp @@ -20,7 +20,7 @@ bool AdhocCommand::is_admin_only() const return this->admin_only; } -void PingStep1(XmppComponent*, AdhocSession&, XmlNode& command_node) +void PingStep1(XmppComponent&, AdhocSession&, XmlNode& command_node) { XmlNode note("note"); note["type"] = "info"; @@ -28,7 +28,7 @@ void PingStep1(XmppComponent*, AdhocSession&, XmlNode& command_node) command_node.add_child(std::move(note)); } -void HelloStep1(XmppComponent*, AdhocSession&, XmlNode& command_node) +void HelloStep1(XmppComponent&, AdhocSession&, XmlNode& command_node) { XmlNode x("jabber:x:data:x"); x["type"] = "form"; @@ -48,11 +48,10 @@ void HelloStep1(XmppComponent*, AdhocSession&, XmlNode& command_node) command_node.add_child(std::move(x)); } -void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node) +void HelloStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node) { // Find out if the name was provided in the form. - const XmlNode* x = command_node.get_child("x", "jabber:x:data"); - if (x) + if (const XmlNode* x = command_node.get_child("x", "jabber:x:data")) { const XmlNode* name_field = nullptr; for (const XmlNode* field: x->get_children("field", "jabber:x:data")) @@ -63,8 +62,7 @@ void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node) } if (name_field) { - const XmlNode* value = name_field->get_child("value", "jabber:x:data"); - if (value) + if (const XmlNode* value = name_field->get_child("value", "jabber:x:data")) { XmlNode note("note"); note["type"] = "info"; @@ -84,7 +82,7 @@ void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node) session.terminate(); } -void Reload(XmppComponent*, AdhocSession&, XmlNode& command_node) +void Reload(XmppComponent&, AdhocSession&, XmlNode& command_node) { ::reload_process(); command_node.delete_all_children(); diff --git a/louloulibs/xmpp/adhoc_command.hpp b/louloulibs/xmpp/adhoc_command.hpp index 1ff2bcf..a2e033a 100644 --- a/louloulibs/xmpp/adhoc_command.hpp +++ b/louloulibs/xmpp/adhoc_command.hpp @@ -35,9 +35,9 @@ private: const bool admin_only; }; -void PingStep1(XmppComponent*, AdhocSession& session, XmlNode& command_node); -void HelloStep1(XmppComponent*, AdhocSession& session, XmlNode& command_node); -void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node); -void Reload(XmppComponent*, AdhocSession& session, XmlNode& command_node); +void PingStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node); +void HelloStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node); +void HelloStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node); +void Reload(XmppComponent&, AdhocSession& session, XmlNode& command_node); #endif // ADHOC_COMMAND_HPP diff --git a/louloulibs/xmpp/adhoc_commands_handler.hpp b/louloulibs/xmpp/adhoc_commands_handler.hpp index cf9ca17..0614b2f 100644 --- a/louloulibs/xmpp/adhoc_commands_handler.hpp +++ b/louloulibs/xmpp/adhoc_commands_handler.hpp @@ -16,7 +16,7 @@ class AdhocCommandsHandler { public: - explicit AdhocCommandsHandler(XmppComponent* xmpp_component): + explicit AdhocCommandsHandler(XmppComponent& xmpp_component): xmpp_component(xmpp_component), commands{} { } @@ -50,10 +50,9 @@ public: void remove_session(const std::string& session_id, const std::string& initiator_jid); private: /** - * A pointer to the XmppComponent, to access to basically anything in the - * gateway. + * To access basically anything in the gateway. */ - XmppComponent* xmpp_component; + XmppComponent& xmpp_component; /** * The list of all available commands. */ diff --git a/louloulibs/xmpp/adhoc_session.hpp b/louloulibs/xmpp/adhoc_session.hpp index 7f07dc1..e98b6a8 100644 --- a/louloulibs/xmpp/adhoc_session.hpp +++ b/louloulibs/xmpp/adhoc_session.hpp @@ -17,7 +17,7 @@ class AdhocSession; * XmlNode and modifies it accordingly (inserting for example an * node, or a data form…). */ -using AdhocStep = std::function; +using AdhocStep = std::function; class AdhocSession { diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index ce9552f..a82892d 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -45,7 +45,7 @@ XmppComponent::XmppComponent(std::shared_ptr poller, const std::string& doc_open(false), served_hostname(hostname), stanza_handlers{}, - adhoc_commands_handler(this) + adhoc_commands_handler(*this) { this->parser.add_stream_open_callback(std::bind(&XmppComponent::on_remote_stream_open, this, std::placeholders::_1)); -- cgit v1.2.3 From 9ac0d3a5766494c9c0c2074c4a21542eea195a29 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 22 Dec 2015 21:37:29 +0100 Subject: A few cleanups, and make a few things more modern --- louloulibs/config/config.hpp | 18 ++++++------------ louloulibs/xmpp/xmpp_parser.cpp | 6 +++--- louloulibs/xmpp/xmpp_parser.hpp | 2 +- louloulibs/xmpp/xmpp_stanza.cpp | 10 ++++++++-- louloulibs/xmpp/xmpp_stanza.hpp | 9 +++++---- 5 files changed, 23 insertions(+), 22 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/config/config.hpp b/louloulibs/config/config.hpp index e070816..688c081 100644 --- a/louloulibs/config/config.hpp +++ b/louloulibs/config/config.hpp @@ -7,7 +7,7 @@ * If you want to exit if the file does not exist when it is open for * reading, set Config::file_must_exist = true. * - * Config::get() can the be used to access the values in the conf. + * Config::get() can then be used to access the values in the conf. * * Use Config::close() when you're done getting/setting value. This will * save the config into the file. @@ -33,35 +33,29 @@ public: /** * returns a value from the config. If it doesn’t exist, use * the second argument as the default. - * @param option The option we want - * @param def The default value in case the option does not exist */ static std::string get(const std::string&, const std::string&); /** * returns a value from the config. If it doesn’t exist, use * the second argument as the default. - * @param option The option we want - * @param def The default value in case the option does not exist */ static int get_int(const std::string&, const int&); /** * Set a value for the given option. And write all the config - * in the file from which it was read if boolean is set. - * @param option The option to set - * @param value The value to use - * @param save if true, save the config file + * in the file from which it was read if save is true. */ static void set(const std::string&, const std::string&, bool save = false); /** * Adds a function to a list. This function will be called whenever a - * configuration change occurs. + * configuration change occurs (when set() is called, or when the initial + * conf is read) */ static void connect(t_config_changed_callback); /** - * Close the config file, saving it to the file is save == true. + * Destroy the instance, forcing it to be recreated (with potentially + * different parameters) the next time it’s needed. */ static void close(); - /** * Set the value of the filename to use, before calling any method. */ diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp index 25f2876..69de145 100644 --- a/louloulibs/xmpp/xmpp_parser.cpp +++ b/louloulibs/xmpp/xmpp_parser.cpp @@ -124,12 +124,12 @@ void XmppParser::end_element(const XML_Char*) } } -void XmppParser::char_data(const XML_Char* data, int len) +void XmppParser::char_data(const XML_Char* data, const size_t len) { if (this->current_node->has_children()) - this->current_node->get_last_child()->add_to_tail(std::string(data, len)); + this->current_node->get_last_child()->add_to_tail({data, len}); else - this->current_node->add_to_inner(std::string(data, len)); + this->current_node->add_to_inner({data, len}); } void XmppParser::stanza_event(const Stanza& stanza) const diff --git a/louloulibs/xmpp/xmpp_parser.hpp b/louloulibs/xmpp/xmpp_parser.hpp index 4de639d..3474b13 100644 --- a/louloulibs/xmpp/xmpp_parser.hpp +++ b/louloulibs/xmpp/xmpp_parser.hpp @@ -82,7 +82,7 @@ public: /** * Some inner or tail data has been parsed */ - void char_data(const XML_Char* data, int len); + void char_data(const XML_Char* data, const size_t len); /** * Calls all the stanza_callbacks one by one. */ diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp index d1c2e0f..f247436 100644 --- a/louloulibs/xmpp/xmpp_stanza.cpp +++ b/louloulibs/xmpp/xmpp_stanza.cpp @@ -199,6 +199,11 @@ void XmlNode::set_name(const std::string& name) this->name = name; } +void XmlNode::set_name(std::string&& name) +{ + this->name = std::move(name); +} + const std::string XmlNode::get_name() const { return this->name; @@ -228,7 +233,7 @@ bool XmlNode::has_children() const return !this->children.empty(); } -const std::string XmlNode::get_tag(const std::string& name) const +const std::string& XmlNode::get_tag(const std::string& name) const { try { @@ -237,7 +242,8 @@ const std::string XmlNode::get_tag(const std::string& name) const } catch (const std::out_of_range& e) { - return ""; + static const std::string def{}; + return def; } } diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp index b1ba54a..bdc937f 100644 --- a/louloulibs/xmpp/xmpp_stanza.hpp +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -96,6 +96,7 @@ public: XmlNode* get_last_child() const; XmlNode* get_parent() const; void set_name(const std::string& name); + void set_name(std::string&& name); const std::string get_name() const; /** * Serialize the stanza into a string @@ -110,7 +111,7 @@ public: * Gets the value for the given attribute, returns an empty string if the * node as no such attribute. */ - const std::string get_tag(const std::string& name) const; + const std::string& get_tag(const std::string& name) const; /** * Remove the attribute of the node. Does nothing if that attribute is not * present. Returns true if the tag was removed, false if it was absent. @@ -133,13 +134,13 @@ private: XmlNode& operator=(XmlNode&&) = delete; }; +std::ostream& operator<<(std::ostream& os, const XmlNode& node); + /** * An XMPP stanza is just an XML node of level 2 in the XMPP document (the * level 1 ones are the , and the ones above 2 are just the * content of the stanzas) */ -typedef XmlNode Stanza; - -std::ostream& operator<<(std::ostream& os, const XmlNode& node); +using Stanza = XmlNode; #endif // XMPP_STANZA_INCLUDED -- cgit v1.2.3 From 79cdf170d2ab6c5378cfbf61d5c8888a4c666190 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 28 Dec 2015 21:25:48 +0100 Subject: Use the configured encoding value when decoding received messages --- louloulibs/xmpp/xmpp_stanza.cpp | 16 ++++++++-------- louloulibs/xmpp/xmpp_stanza.hpp | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp index f247436..407e631 100644 --- a/louloulibs/xmpp/xmpp_stanza.cpp +++ b/louloulibs/xmpp/xmpp_stanza.cpp @@ -84,6 +84,14 @@ std::string xml_unescape(const std::string& data) return res; } +std::string sanitize(const std::string& data, const std::string& encoding) +{ + if (utils::is_valid_utf8(data.data())) + return xml_escape(utils::remove_invalid_xml_chars(data)); + else + return xml_escape(utils::remove_invalid_xml_chars(utils::convert_to_utf8(data, encoding.data()))); +} + XmlNode::XmlNode(const std::string& name, XmlNode* parent): parent(parent) { @@ -259,14 +267,6 @@ std::string& XmlNode::operator[](const std::string& name) return this->attributes[name]; } -std::string sanitize(const std::string& data) -{ - if (utils::is_valid_utf8(data.data())) - return xml_escape(utils::remove_invalid_xml_chars(data)); - else - return xml_escape(utils::remove_invalid_xml_chars(utils::convert_to_utf8(data, "ISO-8859-1"))); -} - std::ostream& operator<<(std::ostream& os, const XmlNode& node) { return os << node.to_string(); diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp index bdc937f..77ab206 100644 --- a/louloulibs/xmpp/xmpp_stanza.hpp +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -8,7 +8,7 @@ std::string xml_escape(const std::string& data); std::string xml_unescape(const std::string& data); -std::string sanitize(const std::string& data); +std::string sanitize(const std::string& data, const std::string& encoding = "ISO-8859-1"); /** * Represent an XML node. It has -- cgit v1.2.3 From 1f8333f23f060750673d0b7c573f2e2d12142adf Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 30 Dec 2015 21:34:11 +0100 Subject: Support a trusted SHA1 fingerprint to be configured for each IRC server --- louloulibs/network/credentials_manager.cpp | 15 ++++++++++++++- louloulibs/network/credentials_manager.hpp | 2 ++ louloulibs/network/tcp_socket_handler.hpp | 2 ++ 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index 57100ee..f92aef8 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -26,11 +26,17 @@ bool Basic_Credentials_Manager::certs_loaded = false; Basic_Credentials_Manager::Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler): Botan::Credentials_Manager(), - socket_handler(socket_handler) + socket_handler(socket_handler), + trusted_fingerprint{} { this->load_certs(); } +void Basic_Credentials_Manager::set_trusted_fingerprint(const std::string& fingerprint) +{ + this->trusted_fingerprint = fingerprint; +} + void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector& certs) @@ -44,6 +50,13 @@ void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type catch (const std::exception& tls_exception) { log_warning("TLS certificate check failed: " << tls_exception.what()); + if (!this->trusted_fingerprint.empty() && !certs.empty() && + this->trusted_fingerprint == certs[0].fingerprint() && + certs[0].matches_dns_name(purported_hostname)) + // We trust the certificate, based on the trusted fingerprint and + // the fact that the hostname matches + return; + if (this->socket_handler->abort_on_invalid_cert()) throw; } diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index e292321..dbd1d95 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -19,6 +19,7 @@ public: const std::vector&) override final; std::vector trusted_certificate_authorities(const std::string& type, const std::string& context) override final; + void set_trusted_fingerprint(const std::string& fingerprint); private: const TCPSocketHandler* const socket_handler; @@ -26,6 +27,7 @@ private: static void load_certs(); static Botan::Certificate_Store_In_Memory certificate_store; static bool certs_loaded; + std::string trusted_fingerprint; }; #endif //BOTAN_FOUND diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 9f5caa3..fb4195d 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -248,7 +248,9 @@ private: static Botan::AutoSeeded_RNG rng; static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; +protected: Basic_Credentials_Manager credential_manager; +private: /** * We use a unique_ptr because we may not want to create the object at * all. The Botan::TLS::Client object generates a handshake message and -- cgit v1.2.3 From 0864f2ab99073b62e4173da694a13aa4ccc8b554 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 14 Jan 2016 00:39:08 +0100 Subject: Quiet a warning --- louloulibs/network/tcp_socket_handler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 6ed981c..81369dd 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -63,14 +63,14 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) // used in bind() struct addrinfo* result; int err = ::getaddrinfo(this->bind_addr.data(), nullptr, nullptr, &result); - if (err != 0) + if (err != 0 || !result) log_error("Failed to bind socket to " << this->bind_addr << ": " << gai_strerror(err)); else { utils::ScopeGuard sg([result](){ freeaddrinfo(result); }); struct addrinfo* rp; - int bind_error; + int bind_error = 0; for (rp = result; rp; rp = rp->ai_next) { if ((bind_error = ::bind(this->socket, -- cgit v1.2.3 From a13285d0ff360d0a83e007a776d9efbcfc347c76 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 10 Feb 2016 20:10:40 +0100 Subject: Rename BasicCredentialManager --- louloulibs/network/credentials_manager.cpp | 24 ++++++++++++------------ louloulibs/network/credentials_manager.hpp | 4 ++-- louloulibs/network/tcp_socket_handler.hpp | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index f92aef8..d5b0eb3 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -21,10 +21,10 @@ static const std::vector default_cert_files = { "/etc/ca-certificates/extracted/tls-ca-bundle.pem" }; -Botan::Certificate_Store_In_Memory Basic_Credentials_Manager::certificate_store; -bool Basic_Credentials_Manager::certs_loaded = false; +Botan::Certificate_Store_In_Memory BasicCredentialsManager::certificate_store; +bool BasicCredentialsManager::certs_loaded = false; -Basic_Credentials_Manager::Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler): +BasicCredentialsManager::BasicCredentialsManager(const TCPSocketHandler* const socket_handler): Botan::Credentials_Manager(), socket_handler(socket_handler), trusted_fingerprint{} @@ -32,14 +32,14 @@ Basic_Credentials_Manager::Basic_Credentials_Manager(const TCPSocketHandler* con this->load_certs(); } -void Basic_Credentials_Manager::set_trusted_fingerprint(const std::string& fingerprint) +void BasicCredentialsManager::set_trusted_fingerprint(const std::string& fingerprint) { this->trusted_fingerprint = fingerprint; } -void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type, - const std::string& purported_hostname, - const std::vector& certs) +void BasicCredentialsManager::verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector& certs) { log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); try @@ -62,10 +62,10 @@ void Basic_Credentials_Manager::verify_certificate_chain(const std::string& type } } -void Basic_Credentials_Manager::load_certs() +void BasicCredentialsManager::load_certs() { // Only load the certificates the first time - if (Basic_Credentials_Manager::certs_loaded) + if (BasicCredentialsManager::certs_loaded) return; const std::string conf_path = Config::get("ca_file", ""); std::vector paths; @@ -82,7 +82,7 @@ void Basic_Credentials_Manager::load_certs() while (!bundle.end_of_data() && bundle.check_available(27)) { const Botan::X509_Certificate cert(bundle); - Basic_Credentials_Manager::certificate_store.add_certificate(cert); + BasicCredentialsManager::certificate_store.add_certificate(cert); } // Only use the first file that can successfully be read. goto success; @@ -95,10 +95,10 @@ void Basic_Credentials_Manager::load_certs() // If we could not open one of the files, print a warning log_warning("The CA could not be loaded, TLS negociation will probably fail."); success: - Basic_Credentials_Manager::certs_loaded = true; + BasicCredentialsManager::certs_loaded = true; } -std::vector Basic_Credentials_Manager::trusted_certificate_authorities(const std::string&, const std::string&) +std::vector BasicCredentialsManager::trusted_certificate_authorities(const std::string&, const std::string&) { return {&this->certificate_store}; } diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index dbd1d95..153c4d6 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -10,10 +10,10 @@ class TCPSocketHandler; -class Basic_Credentials_Manager: public Botan::Credentials_Manager +class BasicCredentialsManager: public Botan::Credentials_Manager { public: - Basic_Credentials_Manager(const TCPSocketHandler* const socket_handler); + BasicCredentialsManager(const TCPSocketHandler* const socket_handler); void verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector&) override final; diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index fb4195d..62b2ce7 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -249,7 +249,7 @@ private: static Botan::TLS::Policy policy; static Botan::TLS::Session_Manager_In_Memory session_manager; protected: - Basic_Credentials_Manager credential_manager; + BasicCredentialsManager credential_manager; private: /** * We use a unique_ptr because we may not want to create the object at -- cgit v1.2.3 From 218260362f0ccb4fd5b51765d4bd331389f39baa Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 10 Feb 2016 20:22:51 +0100 Subject: Remove unused xml_unescape() function --- louloulibs/xmpp/xmpp_stanza.cpp | 44 ----------------------------------------- 1 file changed, 44 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp index 407e631..ac6ce9b 100644 --- a/louloulibs/xmpp/xmpp_stanza.cpp +++ b/louloulibs/xmpp/xmpp_stanza.cpp @@ -40,50 +40,6 @@ std::string xml_escape(const std::string& data) return res; } -std::string xml_unescape(const std::string& data) -{ - std::string res; - res.reserve(data.size()); - const char* str = data.c_str(); - while (str && *str && static_cast(str - data.c_str()) < data.size()) - { - if (*str == '&') - { - if (strncmp(str+1, "amp;", 4) == 0) - { - res += "&"; - str += 4; - } - else if (strncmp(str+1, "lt;", 3) == 0) - { - res += "<"; - str += 3; - } - else if (strncmp(str+1, "gt;", 3) == 0) - { - res += ">"; - str += 3; - } - else if (strncmp(str+1, "quot;", 5) == 0) - { - res += "\""; - str += 5; - } - else if (strncmp(str+1, "apos;", 5) == 0) - { - res += "'"; - str += 5; - } - else - res += "&"; - } - else - res += *str; - str++; - } - return res; -} - std::string sanitize(const std::string& data, const std::string& encoding) { if (utils::is_valid_utf8(data.data())) -- cgit v1.2.3 From 6ee80ad26858eeee9a99b448320025ca439eee55 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 22 Feb 2016 16:56:13 +0100 Subject: Fix the ordering of poll callbacks (recv, connect, send) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because if we have a send event to signal the connection sucess + a recv event to signal something to read on the socket, we need to first finish the connect process before reading the available data. That’s what we do now. --- louloulibs/network/poller.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 329e1c8..50e9806 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -205,15 +205,12 @@ int Poller::poll(const std::chrono::milliseconds& timeout) for (int i = 0; i < nb_events; ++i) { auto socket_handler = static_cast(revents[i].data.ptr); - if (revents[i].events & EPOLLIN) + if (revents[i].events & EPOLLIN && socket_handler->is_connected()) socket_handler->on_recv(); + else if (revents[i].events & EPOLLOUT && socket_handler->is_connected()) + socket_handler->on_send(); else if (revents[i].events & EPOLLOUT) - { - if (socket_handler->is_connected()) - socket_handler->on_send(); - else - socket_handler->connect(); - } + socket_handler->connect(); } return nb_events; #endif -- cgit v1.2.3 From 37fd6ff18a6e51ee5c4127b7e1269e3944d71b44 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 22 Feb 2016 17:01:10 +0100 Subject: log the handshake + stream opening/close in a more consistent way --- louloulibs/xmpp/xmpp_component.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index a82892d..c5906e5 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -89,9 +89,10 @@ void XmppComponent::on_connected() { log_info("connected to XMPP server"); this->first_connection_try = true; - this->send_data(""); + auto data = ""; + log_debug("XMPP SENDING: " << data); + this->send_data(std::move(data)); this->doc_open = true; // We may have some pending data to send: this happens when we try to send // some data before we are actually connected. We send that data right now, if any @@ -124,7 +125,7 @@ void XmppComponent::parse_in_buffer(const size_t size) void XmppComponent::on_remote_stream_open(const XmlNode& node) { - log_debug("XMPP DOCUMENT OPEN: " << node.to_string()); + log_debug("XMPP RECEIVING: " << node.to_string()); this->stream_id = node.get_tag("id"); if (this->stream_id.empty()) { @@ -145,14 +146,14 @@ void XmppComponent::on_remote_stream_open(const XmlNode& node) sprintf(digest + (i*2), "%02x", result[i]); digest[HASH_LENGTH * 2] = '\0'; - this->send_data(""); - this->send_data(digest); - this->send_data(""); + auto data = ""s + digest + ""; + log_debug("XMPP SENDING: " << data); + this->send_data(std::move(data)); } void XmppComponent::on_remote_stream_close(const XmlNode& node) { - log_debug("XMPP DOCUMENT CLOSE " << node.to_string()); + log_debug("XMPP RECEIVING: " << node.to_string()); this->doc_open = false; } -- cgit v1.2.3 From b29d94444d176604f0847985f3ecf57091d4a47b Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 24 Feb 2016 09:28:59 +0100 Subject: Close the epoll fd --- louloulibs/network/poller.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 50e9806..0559644 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,8 @@ Poller::Poller() Poller::~Poller() { + if (this->epfd > 0) + ::close(this->epfd); } void Poller::add_socket_handler(SocketHandler* socket_handler) -- cgit v1.2.3 From b47179f1256f2a3fe6ce0d8f30eceb0f91b33b2e Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 24 Feb 2016 09:29:28 +0100 Subject: Fix the ordering of poll callbacks, with ppoll too --- louloulibs/network/poller.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 0559644..26e5a8f 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -171,21 +171,22 @@ int Poller::poll(const std::chrono::milliseconds& timeout) assert(static_cast(nb_events) <= this->nfds); for (size_t i = 0; i <= this->nfds && nb_events != 0; ++i) { + auto socket_handler = this->socket_handlers.at(this->fds[i].fd); if (this->fds[i].revents == 0) continue; - else if (this->fds[i].revents & POLLIN) + else if (this->fds[i].revents & POLLIN && socket_handler->is_connected()) { - auto socket_handler = this->socket_handlers.at(this->fds[i].fd); socket_handler->on_recv(); nb_events--; } - else if (this->fds[i].revents & POLLOUT) + else if (this->fds[i].revents & POLLOUT && socket_handler->is_connected()) { - auto socket_handler = this->socket_handlers.at(this->fds[i].fd); - if (socket_handler->is_connected()) socket_handler->on_send(); - else - socket_handler->connect(); + nb_events--; + } + else if (this->fds[i].revents & POLLOUT) + { + socket_handler->connect(); nb_events--; } } -- cgit v1.2.3 From 73ad709ddf5db77e721b7080da64603aab10de17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 11 Apr 2016 17:17:48 +0200 Subject: Add a TEMPORARY work-around for botan 1.11.29 --- louloulibs/network/credentials_manager.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index d5b0eb3..c5b8493 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -81,8 +81,18 @@ void BasicCredentialsManager::load_certs() log_debug("Using ca bundle: " << path); while (!bundle.end_of_data() && bundle.check_available(27)) { - const Botan::X509_Certificate cert(bundle); - BasicCredentialsManager::certificate_store.add_certificate(cert); + // TODO: remove this work-around for Botan 1.11.29 + // https://github.com/randombit/botan/issues/438#issuecomment-192866796 + // Note that every certificate that fails to be transcoded into latin-1 + // will be ignored. As a result, some TLS connection may be refused + // because the certificate is signed by an issuer that was ignored. + try { + const Botan::X509_Certificate cert(bundle); + BasicCredentialsManager::certificate_store.add_certificate(cert); + } catch (const Botan::Decoding_Error& error) + { + continue; + } } // Only use the first file that can successfully be read. goto success; -- cgit v1.2.3 From 04d28f968b227067e77e365d317fc251d3c965f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 19 Apr 2016 02:43:26 +0200 Subject: Forward the topic authors, handle the author from 333 messages fix #2 --- louloulibs/xmpp/xmpp_component.cpp | 7 +++++-- louloulibs/xmpp/xmpp_component.hpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index c5906e5..8a0ca52 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -388,11 +388,14 @@ void XmppComponent::send_invalid_user_error(const std::string& user_name, const this->send_stanza(message); } -void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, const std::string& to) +void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, const std::string& to, const std::string& who) { XmlNode message("message"); message["to"] = to; - message["from"] = from + "@" + this->served_hostname; + if (who.empty()) + message["from"] = from + "@" + this->served_hostname; + else + message["from"] = from + "@" + this->served_hostname + "/" + who; message["type"] = "groupchat"; XmlNode subject("subject"); subject.set_inner(std::get<0>(topic)); diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp index 06236fe..07322dd 100644 --- a/louloulibs/xmpp/xmpp_component.hpp +++ b/louloulibs/xmpp/xmpp_component.hpp @@ -124,7 +124,7 @@ public: /** * Send the MUC topic to the user */ - void send_topic(const std::string& from, Xmpp::body&& xmpp_topic, const std::string& to); + void send_topic(const std::string& from, Xmpp::body&& xmpp_topic, const std::string& to, const std::string& who); /** * Send a (non-private) message to the MUC */ -- cgit v1.2.3 From 1e56c59e8241dbfc6a2526c371cc2e894f9d0f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 25 Apr 2016 11:29:52 +0200 Subject: Include the Configure ad-hoc command on biboumi's JID for fixed_irc_server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because a jid like “freenode.example.org” is both the JID for the configured IRC server, and biboumi’s JID. fix #3175 --- louloulibs/xmpp/adhoc_command.cpp | 4 ---- louloulibs/xmpp/adhoc_command.hpp | 6 +++++- louloulibs/xmpp/xmpp_component.cpp | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/adhoc_command.cpp b/louloulibs/xmpp/adhoc_command.cpp index 8a8bcff..99701d7 100644 --- a/louloulibs/xmpp/adhoc_command.cpp +++ b/louloulibs/xmpp/adhoc_command.cpp @@ -11,10 +11,6 @@ AdhocCommand::AdhocCommand(std::vector&& callbacks, const std::string { } -AdhocCommand::~AdhocCommand() -{ -} - bool AdhocCommand::is_admin_only() const { return this->admin_only; diff --git a/louloulibs/xmpp/adhoc_command.hpp b/louloulibs/xmpp/adhoc_command.hpp index a2e033a..1c4e4de 100644 --- a/louloulibs/xmpp/adhoc_command.hpp +++ b/louloulibs/xmpp/adhoc_command.hpp @@ -19,7 +19,11 @@ class AdhocCommand friend class AdhocSession; public: AdhocCommand(std::vector&& callback, const std::string& name, const bool admin_only); - ~AdhocCommand(); + ~AdhocCommand() = default; + AdhocCommand(const AdhocCommand&) = default; + AdhocCommand(AdhocCommand&&) = default; + AdhocCommand& operator=(AdhocCommand&&) = delete; + AdhocCommand& operator=(const AdhocCommand&) = delete; const std::string name; diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index 8a0ca52..b92d9a3 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -591,7 +591,9 @@ void XmppComponent::send_version(const std::string& id, const std::string& jid_t this->send_stanza(iq); } -void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, const std::string& from_jid, const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler) +void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, + const std::string& from_jid, + const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler) { Stanza iq("iq"); iq["type"] = "result"; -- cgit v1.2.3 From af42073830087d97385e507f27f601e8769541b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 4 May 2016 14:16:40 +0200 Subject: Style fix Move all constructors at the top of classes --- louloulibs/config/config.hpp | 13 +++++++------ louloulibs/logger/logger.hpp | 8 +++++--- louloulibs/network/credentials_manager.hpp | 6 ++++++ louloulibs/network/dns_handler.cpp | 4 +++- louloulibs/network/dns_handler.hpp | 10 +++++----- louloulibs/network/dns_socket_handler.cpp | 4 ---- louloulibs/network/dns_socket_handler.hpp | 11 ++++++----- louloulibs/network/poller.hpp | 9 ++++----- louloulibs/network/resolver.hpp | 9 ++++----- louloulibs/network/socket_handler.hpp | 11 +++++------ louloulibs/network/tcp_socket_handler.hpp | 11 +++++------ louloulibs/utils/scopeguard.hpp | 10 ++++++---- louloulibs/utils/timed_events.cpp | 4 ---- louloulibs/utils/timed_events.hpp | 25 ++++++++++++++----------- louloulibs/utils/timed_events_manager.cpp | 8 -------- louloulibs/xmpp/adhoc_commands_handler.hpp | 11 ++++++----- louloulibs/xmpp/adhoc_session.hpp | 12 ++++++------ louloulibs/xmpp/jid.hpp | 11 +++++------ louloulibs/xmpp/xmpp_component.hpp | 10 +++++----- louloulibs/xmpp/xmpp_parser.cpp | 4 ++-- louloulibs/xmpp/xmpp_parser.hpp | 16 +++++++++------- louloulibs/xmpp/xmpp_stanza.hpp | 8 ++++---- 22 files changed, 107 insertions(+), 108 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/config/config.hpp b/louloulibs/config/config.hpp index 688c081..72620c0 100644 --- a/louloulibs/config/config.hpp +++ b/louloulibs/config/config.hpp @@ -28,8 +28,13 @@ typedef std::function t_config_changed_callback; class Config { public: - Config(){}; - ~Config(){}; + Config() = default; + ~Config() = default; + Config(const Config&) = delete; + Config& operator=(const Config&) = delete; + Config(Config&&) = delete; + Config& operator=(Config&&) = delete; + /** * returns a value from the config. If it doesn’t exist, use * the second argument as the default. @@ -88,10 +93,6 @@ private: std::map values; std::vector callbacks; - Config(const Config&) = delete; - Config& operator=(const Config&) = delete; - Config(Config&&) = delete; - Config& operator=(Config&&) = delete; }; #endif // CONFIG_INCLUDED diff --git a/louloulibs/logger/logger.hpp b/louloulibs/logger/logger.hpp index ab8bdf1..3547513 100644 --- a/louloulibs/logger/logger.hpp +++ b/louloulibs/logger/logger.hpp @@ -67,10 +67,12 @@ public: Logger(const int log_level, const std::string& log_file); Logger(const int log_level); -private: - Logger(const Logger&); - Logger& operator=(const Logger&); + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; + Logger(Logger&&) = delete; + Logger& operator=(Logger&&) = delete; +private: const int log_level; std::ofstream ofstream; nullstream null_stream; diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index 153c4d6..c0d23ae 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -14,6 +14,12 @@ class BasicCredentialsManager: public Botan::Credentials_Manager { public: BasicCredentialsManager(const TCPSocketHandler* const socket_handler); + + BasicCredentialsManager(BasicCredentialsManager&&) = delete; + BasicCredentialsManager(const BasicCredentialsManager&) = delete; + BasicCredentialsManager& operator=(const BasicCredentialsManager&) = delete; + BasicCredentialsManager& operator=(BasicCredentialsManager&&) = delete; + void verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector&) override final; diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index a8d0f9c..5e19f8c 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -13,7 +13,9 @@ DNSHandler DNSHandler::instance; using namespace std::string_literals; -DNSHandler::DNSHandler() +DNSHandler::DNSHandler(): + socket_handlers{}, + channel{nullptr} { int ares_error; if ((ares_error = ::ares_library_init(ARES_LIB_INIT_ALL)) != 0) diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index 61e2302..d2b48d2 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -24,6 +24,11 @@ class DNSHandler public: DNSHandler(); ~DNSHandler() = default; + DNSHandler(const DNSHandler&) = delete; + DNSHandler(DNSHandler&&) = delete; + DNSHandler& operator=(const DNSHandler&) = delete; + DNSHandler& operator=(DNSHandler&&) = delete; + void gethostbyname(const std::string& name, ares_host_callback callback, void* socket_handler, int family); /** @@ -48,11 +53,6 @@ private: */ std::vector> socket_handlers; ares_channel channel; - - DNSHandler(const DNSHandler&) = delete; - DNSHandler(DNSHandler&&) = delete; - DNSHandler& operator=(const DNSHandler&) = delete; - DNSHandler& operator=(DNSHandler&&) = delete; }; #endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp index 124c9b2..faaabdb 100644 --- a/louloulibs/network/dns_socket_handler.cpp +++ b/louloulibs/network/dns_socket_handler.cpp @@ -13,10 +13,6 @@ DNSSocketHandler::DNSSocketHandler(std::shared_ptr poller, { } -DNSSocketHandler::~DNSSocketHandler() -{ -} - void DNSSocketHandler::connect() { } diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index ad119e1..5ea9846 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -18,7 +18,12 @@ class DNSSocketHandler: public SocketHandler { public: explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); - ~DNSSocketHandler(); + ~DNSSocketHandler() = default; + DNSSocketHandler(const DNSSocketHandler&) = delete; + DNSSocketHandler(DNSSocketHandler&&) = delete; + DNSSocketHandler& operator=(const DNSSocketHandler&) = delete; + DNSSocketHandler& operator=(DNSSocketHandler&&) = delete; + /** * Just call dns_process_fd, c-ares will do its work of send()ing or * recv()ing the data it wants on that socket. @@ -36,10 +41,6 @@ public: bool is_connected() const override final; private: - DNSSocketHandler(const DNSSocketHandler&) = delete; - DNSSocketHandler(DNSSocketHandler&&) = delete; - DNSSocketHandler& operator=(const DNSSocketHandler&) = delete; - DNSSocketHandler& operator=(DNSSocketHandler&&) = delete; }; #endif // CARES_FOUND diff --git a/louloulibs/network/poller.hpp b/louloulibs/network/poller.hpp index de0cb48..4bf7432 100644 --- a/louloulibs/network/poller.hpp +++ b/louloulibs/network/poller.hpp @@ -38,6 +38,10 @@ class Poller public: explicit Poller(); ~Poller(); + Poller(const Poller&) = delete; + Poller(Poller&&) = delete; + Poller& operator=(const Poller&) = delete; + Poller& operator=(Poller&&) = delete; /** * Add a SocketHandler to be monitored by this Poller. All receive events * are always automatically watched. @@ -85,11 +89,6 @@ private: #elif POLLER == EPOLL int epfd; #endif - - Poller(const Poller&) = delete; - Poller(Poller&&) = delete; - Poller& operator=(const Poller&) = delete; - Poller& operator=(Poller&&) = delete; }; #endif // POLLER_INCLUDED diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index f9e9134..fd57e25 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -37,6 +37,10 @@ public: Resolver(); ~Resolver() = default; + Resolver(const Resolver&) = delete; + Resolver(Resolver&&) = delete; + Resolver& operator=(const Resolver&) = delete; + Resolver& operator=(Resolver&&) = delete; bool is_resolving() const { @@ -117,11 +121,6 @@ private: ErrorCallbackType error_cb; SuccessCallbackType success_cb; - - Resolver(const Resolver&) = delete; - Resolver(Resolver&&) = delete; - Resolver& operator=(const Resolver&) = delete; - Resolver& operator=(Resolver&&) = delete; }; std::string addr_to_string(const struct addrinfo* rp); diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index d01ac5d..daa4413 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -16,6 +16,11 @@ public: socket(socket) {} virtual ~SocketHandler() {} + SocketHandler(const SocketHandler&) = delete; + SocketHandler(SocketHandler&&) = delete; + SocketHandler& operator=(const SocketHandler&) = delete; + SocketHandler& operator=(SocketHandler&&) = delete; + virtual void on_recv() = 0; virtual void on_send() = 0; virtual void connect() = 0; @@ -34,12 +39,6 @@ protected: * The handled socket. */ socket_t socket; - -private: - SocketHandler(const SocketHandler&) = delete; - SocketHandler(SocketHandler&&) = delete; - SocketHandler& operator=(const SocketHandler&) = delete; - SocketHandler& operator=(SocketHandler&&) = delete; }; #endif // SOCKET_HANDLER_HPP diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 62b2ce7..dd4e82e 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -28,9 +28,13 @@ class TCPSocketHandler: public SocketHandler { protected: ~TCPSocketHandler(); - public: explicit TCPSocketHandler(std::shared_ptr poller); + TCPSocketHandler(const TCPSocketHandler&) = delete; + TCPSocketHandler(TCPSocketHandler&&) = delete; + TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; + TCPSocketHandler& operator=(TCPSocketHandler&&) = delete; + /** * Connect to the remote server, and call on_connected() if this * succeeds. If tls is true, we set use_tls to true and will also call @@ -231,11 +235,6 @@ protected: std::string bind_addr; private: - TCPSocketHandler(const TCPSocketHandler&) = delete; - TCPSocketHandler(TCPSocketHandler&&) = delete; - TCPSocketHandler& operator=(const TCPSocketHandler&) = delete; - TCPSocketHandler& operator=(TCPSocketHandler&&) = delete; - /** * Display the resolved IP, just for information purpose. */ diff --git a/louloulibs/utils/scopeguard.hpp b/louloulibs/utils/scopeguard.hpp index df78831..fed40ff 100644 --- a/louloulibs/utils/scopeguard.hpp +++ b/louloulibs/utils/scopeguard.hpp @@ -40,6 +40,12 @@ public: { this->add_callback(std::move(func)); } + + ScopeGuard(const ScopeGuard&) = delete; + ScopeGuard& operator=(ScopeGuard&&) = delete; + ScopeGuard(ScopeGuard&&) = delete; + ScopeGuard& operator=(const ScopeGuard&) = delete; + /** * default constructor, the scope guard is enabled but empty, use * add_callback() @@ -78,10 +84,6 @@ private: bool enabled; std::vector> callbacks; - ScopeGuard(const ScopeGuard&) = delete; - ScopeGuard& operator=(ScopeGuard&&) = delete; - ScopeGuard(ScopeGuard&&) = delete; - ScopeGuard& operator=(const ScopeGuard&) = delete; }; } diff --git a/louloulibs/utils/timed_events.cpp b/louloulibs/utils/timed_events.cpp index 5010a3f..930380b 100644 --- a/louloulibs/utils/timed_events.cpp +++ b/louloulibs/utils/timed_events.cpp @@ -29,10 +29,6 @@ TimedEvent::TimedEvent(TimedEvent&& other): { } -TimedEvent::~TimedEvent() -{ -} - bool TimedEvent::is_after(const TimedEvent& other) const { return this->is_after(other.time_point); diff --git a/louloulibs/utils/timed_events.hpp b/louloulibs/utils/timed_events.hpp index 4e2800c..c3dfc40 100644 --- a/louloulibs/utils/timed_events.hpp +++ b/louloulibs/utils/timed_events.hpp @@ -31,7 +31,12 @@ public: std::function callback, const std::string& name=""); explicit TimedEvent(TimedEvent&&); - ~TimedEvent(); + ~TimedEvent() = default; + + TimedEvent(const TimedEvent&) = delete; + TimedEvent& operator=(const TimedEvent&) = delete; + TimedEvent& operator=(TimedEvent&&) = delete; + /** * Whether or not this event happens after the other one. */ @@ -70,10 +75,6 @@ private: * unique. */ const std::string name; - - TimedEvent(const TimedEvent&) = delete; - TimedEvent& operator=(const TimedEvent&) = delete; - TimedEvent& operator=(TimedEvent&&) = delete; }; /** @@ -84,7 +85,13 @@ private: class TimedEventsManager { public: - ~TimedEventsManager(); + ~TimedEventsManager() = default; + + TimedEventsManager(const TimedEventsManager&) = delete; + TimedEventsManager(TimedEventsManager&&) = delete; + TimedEventsManager& operator=(const TimedEventsManager&) = delete; + TimedEventsManager& operator=(TimedEventsManager&&) = delete; + /** * Return the unique instance of this class */ @@ -121,12 +128,8 @@ public: std::size_t size() const; private: - explicit TimedEventsManager(); std::list events; - TimedEventsManager(const TimedEventsManager&) = delete; - TimedEventsManager(TimedEventsManager&&) = delete; - TimedEventsManager& operator=(const TimedEventsManager&) = delete; - TimedEventsManager& operator=(TimedEventsManager&&) = delete; + explicit TimedEventsManager() = default; }; #endif // TIMED_EVENTS_HPP diff --git a/louloulibs/utils/timed_events_manager.cpp b/louloulibs/utils/timed_events_manager.cpp index 2c75e48..b90a237 100644 --- a/louloulibs/utils/timed_events_manager.cpp +++ b/louloulibs/utils/timed_events_manager.cpp @@ -6,14 +6,6 @@ TimedEventsManager& TimedEventsManager::instance() return inst; } -TimedEventsManager::TimedEventsManager() -{ -} - -TimedEventsManager::~TimedEventsManager() -{ -} - void TimedEventsManager::add_event(TimedEvent&& event) { for (auto it = this->events.begin(); it != this->events.end(); ++it) diff --git a/louloulibs/xmpp/adhoc_commands_handler.hpp b/louloulibs/xmpp/adhoc_commands_handler.hpp index 0614b2f..65b094d 100644 --- a/louloulibs/xmpp/adhoc_commands_handler.hpp +++ b/louloulibs/xmpp/adhoc_commands_handler.hpp @@ -21,6 +21,12 @@ public: commands{} { } ~AdhocCommandsHandler() = default; + + AdhocCommandsHandler(const AdhocCommandsHandler&) = delete; + AdhocCommandsHandler(AdhocCommandsHandler&&) = delete; + AdhocCommandsHandler& operator=(const AdhocCommandsHandler&) = delete; + AdhocCommandsHandler& operator=(AdhocCommandsHandler&&) = delete; + /** * Returns the list of available commands. */ @@ -63,11 +69,6 @@ private: * Of the form: {{session_id, owner_jid}, session}. */ std::map, AdhocSession> sessions; - - AdhocCommandsHandler(const AdhocCommandsHandler&) = delete; - AdhocCommandsHandler(AdhocCommandsHandler&&) = delete; - AdhocCommandsHandler& operator=(const AdhocCommandsHandler&) = delete; - AdhocCommandsHandler& operator=(AdhocCommandsHandler&&) = delete; }; #endif // ADHOC_COMMANDS_HANDLER_HPP diff --git a/louloulibs/xmpp/adhoc_session.hpp b/louloulibs/xmpp/adhoc_session.hpp index e98b6a8..0966c4e 100644 --- a/louloulibs/xmpp/adhoc_session.hpp +++ b/louloulibs/xmpp/adhoc_session.hpp @@ -25,6 +25,12 @@ public: explicit AdhocSession(const AdhocCommand& command, const std::string& owner_jid, const std::string& to_jid); ~AdhocSession() = default; + + AdhocSession(const AdhocSession&) = delete; + AdhocSession(AdhocSession&&) = delete; + AdhocSession& operator=(const AdhocSession&) = delete; + AdhocSession& operator=(AdhocSession&&) = delete; + /** * Return the function to be executed, found in our AdhocCommand, for the * current_step. And increment the current_step. @@ -80,12 +86,6 @@ public: * any key in there. */ std::map vars; - -private: - AdhocSession(const AdhocSession&) = delete; - AdhocSession(AdhocSession&&) = delete; - AdhocSession& operator=(const AdhocSession&) = delete; - AdhocSession& operator=(AdhocSession&&) = delete; }; #endif // ADHOC_SESSION_HPP diff --git a/louloulibs/xmpp/jid.hpp b/louloulibs/xmpp/jid.hpp index e2ee586..5ff5a4f 100644 --- a/louloulibs/xmpp/jid.hpp +++ b/louloulibs/xmpp/jid.hpp @@ -11,6 +11,11 @@ class Jid public: explicit Jid(const std::string& jid); + Jid(const Jid&) = delete; + Jid(Jid&&) = delete; + Jid& operator=(const Jid&) = delete; + Jid& operator=(Jid&&) = delete; + std::string domain; std::string local; std::string resource; @@ -23,12 +28,6 @@ public: { return this->local + "@" + this->domain + "/" + this->resource; } - -private: - Jid(const Jid&) = delete; - Jid(Jid&&) = delete; - Jid& operator=(const Jid&) = delete; - Jid& operator=(Jid&&) = delete; }; /** diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp index 07322dd..913e337 100644 --- a/louloulibs/xmpp/xmpp_component.hpp +++ b/louloulibs/xmpp/xmpp_component.hpp @@ -38,6 +38,11 @@ public: explicit XmppComponent(std::shared_ptr poller, const std::string& hostname, const std::string& secret); virtual ~XmppComponent() = default; + XmppComponent(const XmppComponent&) = delete; + XmppComponent(XmppComponent&&) = delete; + XmppComponent& operator=(const XmppComponent&) = delete; + XmppComponent& operator=(XmppComponent&&) = delete; + void on_connection_failed(const std::string& reason) override final; void on_connected() override final; void on_connection_close(const std::string& error) override final; @@ -231,11 +236,6 @@ protected: std::unordered_map> stanza_handlers; AdhocCommandsHandler adhoc_commands_handler; - - XmppComponent(const XmppComponent&) = delete; - XmppComponent(XmppComponent&&) = delete; - XmppComponent& operator=(const XmppComponent&) = delete; - XmppComponent& operator=(XmppComponent&&) = delete; }; #endif // XMPP_COMPONENT_INCLUDED diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp index 69de145..dc12000 100644 --- a/louloulibs/xmpp/xmpp_parser.cpp +++ b/louloulibs/xmpp/xmpp_parser.cpp @@ -118,7 +118,7 @@ void XmppParser::end_element(const XML_Char*) this->stanza_event(*this->current_node); // Note: deleting all the children of our parent deletes ourself, // so current_node is an invalid pointer after this line - this->current_node->get_parent()->delete_all_children(); + parent->delete_all_children(); } this->current_node = parent; } @@ -139,7 +139,7 @@ void XmppParser::stanza_event(const Stanza& stanza) const try { callback(stanza); } catch (const std::exception& e) { - log_debug("Unhandled exception: " << e.what()); + log_error("Unhandled exception: " << e.what()); } } } diff --git a/louloulibs/xmpp/xmpp_parser.hpp b/louloulibs/xmpp/xmpp_parser.hpp index 3474b13..93c6c53 100644 --- a/louloulibs/xmpp/xmpp_parser.hpp +++ b/louloulibs/xmpp/xmpp_parser.hpp @@ -32,12 +32,12 @@ class XmppParser public: explicit XmppParser(); ~XmppParser(); + XmppParser(const XmppParser&) = delete; + XmppParser& operator=(const XmppParser&) = delete; + XmppParser(XmppParser&&) = delete; + XmppParser& operator=(XmppParser&&) = delete; public: - /** - * Init the XML parser and install the callbacks - */ - void init_xml_parser(); /** * Feed the parser with some XML data */ @@ -98,6 +98,11 @@ public: void stream_close_event(const XmlNode& node) const; private: + /** + * Init the XML parser and install the callbacks + */ + void init_xml_parser(); + /** * Expat structure. */ @@ -123,9 +128,6 @@ private: std::vector> stanza_callbacks; std::vector> stream_open_callbacks; std::vector> stream_close_callbacks; - - XmppParser(const XmppParser&) = delete; - XmppParser& operator=(const XmppParser&) = delete; }; #endif // XMPP_PARSER_INCLUDED diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp index 77ab206..28b8414 100644 --- a/louloulibs/xmpp/xmpp_stanza.hpp +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -25,7 +25,6 @@ class XmlNode public: explicit XmlNode(const std::string& name, XmlNode* parent); explicit XmlNode(const std::string& name); - XmlNode(XmlNode&& node) = default; /** * The copy constructor does not copy the parent attribute. The children * nodes are all copied recursively. @@ -42,6 +41,10 @@ public: this->add_child(std::make_unique(*child)); } + XmlNode(XmlNode&& node) = default; + XmlNode& operator=(const XmlNode&) = delete; + XmlNode& operator=(XmlNode&&) = delete; + ~XmlNode() = default; void delete_all_children(); @@ -129,9 +132,6 @@ private: std::vector> children; std::string inner; std::string tail; - - XmlNode& operator=(const XmlNode&) = delete; - XmlNode& operator=(XmlNode&&) = delete; }; std::ostream& operator<<(std::ostream& os, const XmlNode& node); -- cgit v1.2.3 From ce798b1e8cfce7df69e0e91400c96090447a5ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 10 May 2016 12:48:39 +0200 Subject: Fix build with POLLER=POLL --- louloulibs/network/poller.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 26e5a8f..959567e 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -28,8 +28,10 @@ Poller::Poller() Poller::~Poller() { +#if POLLER == EPOLL if (this->epfd > 0) ::close(this->epfd); +#endif } void Poller::add_socket_handler(SocketHandler* socket_handler) -- cgit v1.2.3 From cef238e7002874939cc5e93cd3c29c130623344b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 11 May 2016 00:56:49 +0200 Subject: Do not use std::endl for each line when saving the conf file --- louloulibs/config/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/config/config.cpp b/louloulibs/config/config.cpp index 3e016f4..c785632 100644 --- a/louloulibs/config/config.cpp +++ b/louloulibs/config/config.cpp @@ -117,6 +117,6 @@ void Config::save_to_file() const return ; } for (auto& it: this->values) - file << it.first << "=" << it.second << std::endl; + file << it.first << "=" << it.second << '\n'; file.close(); } -- cgit v1.2.3 From fdddd447b3976f26a4146ebf91abfc04736a006b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 13 May 2016 01:22:49 +0200 Subject: =?UTF-8?q?Use=20=E2=80=9Cusing=E2=80=9D=20instead=20of=20typedef?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- louloulibs/network/socket_handler.hpp | 2 +- louloulibs/xmpp/body.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index daa4413..feabcf1 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -6,7 +6,7 @@ class Poller; -typedef int socket_t; +using socket_t = int; class SocketHandler { diff --git a/louloulibs/xmpp/body.hpp b/louloulibs/xmpp/body.hpp index 6ac678e..df86feb 100644 --- a/louloulibs/xmpp/body.hpp +++ b/louloulibs/xmpp/body.hpp @@ -6,7 +6,7 @@ namespace Xmpp // Contains: // - an XMPP-valid UTF-8 body // - an XML node representing the XHTML-IM body, or null - typedef std::tuple> body; + using body = std::tuple>; } #endif /* XMPP_BODY_HPP_INCLUDED */ -- cgit v1.2.3 From a641a26327e04016dfd62e1cb1f2141b9364631b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 2 Jun 2016 16:03:28 +0200 Subject: Check the length of the JID parts when copying into the jidprep buffer We trust the XMPP server, but maybe not enough to not check that --- louloulibs/xmpp/jid.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/jid.cpp b/louloulibs/xmpp/jid.cpp index dcd7012..7b62f3e 100644 --- a/louloulibs/xmpp/jid.cpp +++ b/louloulibs/xmpp/jid.cpp @@ -47,7 +47,7 @@ std::string jidprep(const std::string& original) Jid jid(original); char local[max_jid_part_len] = {}; - memcpy(local, jid.local.data(), jid.local.size()); + memcpy(local, jid.local.data(), std::min(max_jid_part_len, jid.local.size())); Stringprep_rc rc = static_cast(::stringprep(local, max_jid_part_len, static_cast(0), stringprep_xmpp_nodeprep)); if (rc != STRINGPREP_OK) @@ -57,7 +57,7 @@ std::string jidprep(const std::string& original) } char domain[max_jid_part_len] = {}; - memcpy(domain, jid.domain.data(), jid.domain.size()); + memcpy(domain, jid.domain.data(), std::min(max_jid_part_len, jid.domain.size())); rc = static_cast(::stringprep(domain, max_jid_part_len, static_cast(0), stringprep_nameprep)); if (rc != STRINGPREP_OK) @@ -81,7 +81,7 @@ std::string jidprep(const std::string& original) // Otherwise, also process the resource part char resource[max_jid_part_len] = {}; - memcpy(resource, jid.resource.data(), jid.resource.size()); + memcpy(resource, jid.resource.data(), std::min(max_jid_part_len, jid.resource.size())); rc = static_cast(::stringprep(resource, max_jid_part_len, static_cast(0), stringprep_xmpp_resourceprep)); if (rc != STRINGPREP_OK) -- cgit v1.2.3 From 66609cfba2b581be52de6096193751d1bb4ec3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 22 May 2016 12:52:05 +0200 Subject: Remove all usage of std::list --- louloulibs/network/tcp_socket_handler.hpp | 2 +- louloulibs/utils/timed_events.cpp | 11 +---------- louloulibs/utils/timed_events.hpp | 18 +++++++++--------- 3 files changed, 11 insertions(+), 20 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index dd4e82e..55f4113 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -180,7 +180,7 @@ private: /** * Where data is added, when we want to send something to the client. */ - std::list out_buf; + std::vector out_buf; /** * DNS resolver */ diff --git a/louloulibs/utils/timed_events.cpp b/louloulibs/utils/timed_events.cpp index 930380b..6b936c4 100644 --- a/louloulibs/utils/timed_events.cpp +++ b/louloulibs/utils/timed_events.cpp @@ -20,15 +20,6 @@ TimedEvent::TimedEvent(std::chrono::milliseconds&& duration, { } -TimedEvent::TimedEvent(TimedEvent&& other): - time_point(std::move(other.time_point)), - callback(std::move(other.callback)), - repeat(other.repeat), - repeat_delay(std::move(other.repeat_delay)), - name(std::move(other.name)) -{ -} - bool TimedEvent::is_after(const TimedEvent& other) const { return this->is_after(other.time_point); @@ -47,7 +38,7 @@ std::chrono::milliseconds TimedEvent::get_timeout() const return std::chrono::duration_cast(this->time_point - now); } -void TimedEvent::execute() +void TimedEvent::execute() const { this->callback(); } diff --git a/louloulibs/utils/timed_events.hpp b/louloulibs/utils/timed_events.hpp index c3dfc40..70e2eff 100644 --- a/louloulibs/utils/timed_events.hpp +++ b/louloulibs/utils/timed_events.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include using namespace std::literals::chrono_literals; @@ -30,12 +30,12 @@ public: explicit TimedEvent(std::chrono::milliseconds&& duration, std::function callback, const std::string& name=""); - explicit TimedEvent(TimedEvent&&); + explicit TimedEvent(TimedEvent&&) = default; + TimedEvent& operator=(TimedEvent&&) = default; ~TimedEvent() = default; TimedEvent(const TimedEvent&) = delete; TimedEvent& operator=(const TimedEvent&) = delete; - TimedEvent& operator=(TimedEvent&&) = delete; /** * Whether or not this event happens after the other one. @@ -48,7 +48,7 @@ public: * returned value is 0 instead. The value cannot then be negative. */ std::chrono::milliseconds get_timeout() const; - void execute(); + void execute() const; const std::string& get_name() const; private: @@ -59,22 +59,22 @@ private: /** * The function to execute. */ - const std::function callback; + std::function callback; /** * Whether or not this events repeats itself until it is destroyed. */ - const bool repeat; + bool repeat; /** * This value is added to the time_point each time the event is executed, * if repeat is true. Otherwise it is ignored. */ - const std::chrono::milliseconds repeat_delay; + std::chrono::milliseconds repeat_delay; /** * A name that is used to identify that event. If you want to find your * event (for example if you want to cancel it), the name should be * unique. */ - const std::string name; + std::string name; }; /** @@ -128,7 +128,7 @@ public: std::size_t size() const; private: - std::list events; + std::vector events; explicit TimedEventsManager() = default; }; -- cgit v1.2.3 From 28300dff5b87d6a45ef5bba2a9b916b39022c7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 8 Jun 2016 10:31:19 +0200 Subject: Add a missing vector include --- louloulibs/network/tcp_socket_handler.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 55f4113..91fa7a8 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -13,11 +13,11 @@ #include #include +#include #include #include #include - /** * An interface, with a series of callbacks that should be implemented in * subclasses that deal with a socket. These callbacks are called on various events -- cgit v1.2.3 From 5a2e61161792cf51209f240e40e28036195f35be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 13 Jun 2016 19:59:17 +0200 Subject: Show off, with some variadic templates, for the logger module --- louloulibs/logger/logger.hpp | 74 ++++++++++++++++++++++++------ louloulibs/network/credentials_manager.cpp | 6 +-- louloulibs/network/poller.cpp | 14 +++--- louloulibs/network/tcp_socket_handler.cpp | 40 ++++++++-------- louloulibs/xmpp/adhoc_commands_handler.cpp | 2 +- louloulibs/xmpp/xmpp_component.cpp | 20 ++++---- louloulibs/xmpp/xmpp_parser.cpp | 6 +-- 7 files changed, 103 insertions(+), 59 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/logger/logger.hpp b/louloulibs/logger/logger.hpp index 3547513..8ff4dcd 100644 --- a/louloulibs/logger/logger.hpp +++ b/louloulibs/logger/logger.hpp @@ -33,21 +33,6 @@ # define __FILENAME__ __FILE__ #endif -#define WHERE\ - __FILENAME__ << ":" << __LINE__ - -#define log_debug(text)\ - Logger::instance()->get_stream(debug_lvl) << SD_DEBUG << WHERE << ":\t" << text << std::endl - -#define log_info(text)\ - Logger::instance()->get_stream(info_lvl) << SD_INFO << WHERE << ":\t" << text << std::endl - -#define log_warning(text)\ - Logger::instance()->get_stream(warning_lvl) << SD_WARNING << WHERE << ":\t" << text << std::endl - -#define log_error(text)\ - Logger::instance()->get_stream(error_lvl) << SD_ERR << WHERE << ":\t" << text << std::endl - /** * Juste a structure representing a stream doing nothing with its input. */ @@ -79,4 +64,63 @@ private: std::ostream stream; }; +#define WHERE __FILENAME__, ":", __LINE__, ":\t" + +namespace logging_details +{ + template + void log(std::ostream& os, const T& arg) + { + os << arg << std::endl; + } + + template + void log(std::ostream& os, const T& first, U&&... rest) + { + os << first; + log(os, std::forward(rest)...); + } + + template + void log_debug(U&&... args) + { + auto& os = Logger::instance()->get_stream(debug_lvl); + os << SD_DEBUG; + log(os, std::forward(args)...); + } + + template + void log_info(U&&... args) + { + auto& os = Logger::instance()->get_stream(info_lvl); + os << SD_INFO; + log(os, std::forward(args)...); + } + + template + void log_warning(U&&... args) + { + auto& os = Logger::instance()->get_stream(warning_lvl); + os << SD_WARNING; + log(os, std::forward(args)...); + } + + template + void log_error(U&&... args) + { + auto& os = Logger::instance()->get_stream(error_lvl); + os << SD_ERR; + log(os, std::forward(args)...); + } +} + +#define log_info(...) logging_details::log_info(WHERE, __VA_ARGS__) + +#define log_warning(...) logging_details::log_warning(WHERE, __VA_ARGS__) + +#define log_error(...) logging_details::log_error(WHERE, __VA_ARGS__) + +#define log_debug(...) logging_details::log_debug(WHERE, __VA_ARGS__) + + #endif // LOGGER_INCLUDED diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp index c5b8493..ee83c3b 100644 --- a/louloulibs/network/credentials_manager.cpp +++ b/louloulibs/network/credentials_manager.cpp @@ -41,7 +41,7 @@ void BasicCredentialsManager::verify_certificate_chain(const std::string& type, const std::string& purported_hostname, const std::vector& certs) { - log_debug("Checking remote certificate (" << type << ") for hostname " << purported_hostname); + log_debug("Checking remote certificate (", type, ") for hostname ", purported_hostname); try { Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs); @@ -49,7 +49,7 @@ void BasicCredentialsManager::verify_certificate_chain(const std::string& type, } catch (const std::exception& tls_exception) { - log_warning("TLS certificate check failed: " << tls_exception.what()); + log_warning("TLS certificate check failed: ", tls_exception.what()); if (!this->trusted_fingerprint.empty() && !certs.empty() && this->trusted_fingerprint == certs[0].fingerprint() && certs[0].matches_dns_name(purported_hostname)) @@ -78,7 +78,7 @@ void BasicCredentialsManager::load_certs() try { Botan::DataSource_Stream bundle(path); - log_debug("Using ca bundle: " << path); + log_debug("Using ca bundle: ", path); while (!bundle.end_of_data() && bundle.check_available(27)) { // TODO: remove this work-around for Botan 1.11.29 diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp index 959567e..8a6fd97 100644 --- a/louloulibs/network/poller.cpp +++ b/louloulibs/network/poller.cpp @@ -20,7 +20,7 @@ Poller::Poller() this->epfd = ::epoll_create1(0); if (this->epfd == -1) { - log_error("epoll failed: " << strerror(errno)); + log_error("epoll failed: ", strerror(errno)); throw std::runtime_error("Could not create epoll instance"); } #endif @@ -54,7 +54,7 @@ void Poller::add_socket_handler(SocketHandler* socket_handler) const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_ADD, socket_handler->get_socket(), &event); if (res == -1) { - log_error("epoll_ctl failed: " << strerror(errno)); + log_error("epoll_ctl failed: ", strerror(errno)); throw std::runtime_error("Could not add socket to epoll"); } #endif @@ -86,7 +86,7 @@ void Poller::remove_socket_handler(const socket_t socket) const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_DEL, socket, nullptr); if (res == -1) { - log_error("epoll_ctl failed: " << strerror(errno)); + log_error("epoll_ctl failed: ", strerror(errno)); throw std::runtime_error("Could not remove socket from epoll"); } #endif @@ -109,7 +109,7 @@ void Poller::watch_send_events(SocketHandler* socket_handler) const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event); if (res == -1) { - log_error("epoll_ctl failed: " << strerror(errno)); + log_error("epoll_ctl failed: ", strerror(errno)); throw std::runtime_error("Could not modify socket flags in epoll"); } #endif @@ -132,7 +132,7 @@ void Poller::stop_watching_send_events(SocketHandler* socket_handler) const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event); if (res == -1) { - log_error("epoll_ctl failed: " << strerror(errno)); + log_error("epoll_ctl failed: ", strerror(errno)); throw std::runtime_error("Could not modify socket flags in epoll"); } #endif @@ -165,7 +165,7 @@ int Poller::poll(const std::chrono::milliseconds& timeout) { if (errno == EINTR) return true; - log_error("poll failed: " << strerror(errno)); + log_error("poll failed: ", strerror(errno)); throw std::runtime_error("Poll failed"); } // We cannot possibly have more ready events than the number of fds we are @@ -205,7 +205,7 @@ int Poller::poll(const std::chrono::milliseconds& timeout) { if (errno == EINTR) return 0; - log_error("epoll wait: " << strerror(errno)); + log_error("epoll wait: ", strerror(errno)); throw std::runtime_error("Epoll_wait failed"); } for (int i = 0; i < nb_events; ++i) diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp index 81369dd..5420b1c 100644 --- a/louloulibs/network/tcp_socket_handler.cpp +++ b/louloulibs/network/tcp_socket_handler.cpp @@ -64,8 +64,8 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) struct addrinfo* result; int err = ::getaddrinfo(this->bind_addr.data(), nullptr, nullptr, &result); if (err != 0 || !result) - log_error("Failed to bind socket to " << this->bind_addr << ": " - << gai_strerror(err)); + log_error("Failed to bind socket to ", this->bind_addr, ": ", + gai_strerror(err)); else { utils::ScopeGuard sg([result](){ freeaddrinfo(result); }); @@ -79,15 +79,15 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp) break; } if (!rp) - log_error("Failed to bind socket to " << this->bind_addr << ": " - << strerror(bind_error)); + log_error("Failed to bind socket to ", this->bind_addr, ": ", + strerror(bind_error)); else - log_info("Socket successfully bound to " << this->bind_addr); + log_info("Socket successfully bound to ", this->bind_addr); } } int optval = 1; if (::setsockopt(this->socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) - log_warning("Failed to enable TCP keepalive on socket: " << strerror(errno)); + log_warning("Failed to enable TCP keepalive on socket: ", strerror(errno)); // Set the socket on non-blocking mode. This is useful to receive a EAGAIN // error when connect() would block, to not block the whole process if a // remote is not responsive. @@ -113,7 +113,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po // this is the first call of this function. if (!this->resolver.is_resolved()) { - log_info("Trying to connect to " << address << ":" << port); + log_info("Trying to connect to ", address, ":", port); // Start the asynchronous process of resolving the hostname. Once // the addresses have been found and `resolved` has been set to true // (but connecting will still be false), TCPSocketHandler::connect() @@ -161,7 +161,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po this->init_socket(rp); } catch (const std::runtime_error& error) { - log_error("Failed to init socket: " << error.what()); + log_error("Failed to init socket: ", error.what()); break; } } @@ -204,7 +204,7 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po "connection_timeout"s + std::to_string(this->socket))); return ; } - log_info("Connection failed:" << strerror(errno)); + log_info("Connection failed:", strerror(errno)); } log_error("All connection attempts failed."); this->close(); @@ -268,9 +268,9 @@ ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size) else if (-1 == size) { if (this->connecting) - log_warning("Error connecting: " << strerror(errno)); + log_warning("Error connecting: ", strerror(errno)); else - log_warning("Error while reading from socket: " << strerror(errno)); + log_warning("Error while reading from socket: ", strerror(errno)); // Remember if we were connecting, or already connected when this // happened, because close() sets this->connecting to false const auto were_connecting = this->connecting; @@ -300,7 +300,7 @@ void TCPSocketHandler::on_send() ssize_t res = ::sendmsg(this->socket, &msg, MSG_NOSIGNAL); if (res < 0) { - log_error("sendmsg failed: " << strerror(errno)); + log_error("sendmsg failed: ", strerror(errno)); this->on_connection_close(strerror(errno)); this->close(); } @@ -351,9 +351,9 @@ void TCPSocketHandler::close() void TCPSocketHandler::display_resolved_ip(struct addrinfo* rp) const { if (rp->ai_family == AF_INET) - log_debug("Trying IPv4 address " << addr_to_string(rp)); + log_debug("Trying IPv4 address ", addr_to_string(rp)); else if (rp->ai_family == AF_INET6) - log_debug("Trying IPv6 address " << addr_to_string(rp)); + log_debug("Trying IPv6 address ", addr_to_string(rp)); } void TCPSocketHandler::send_data(std::string&& data) @@ -478,18 +478,18 @@ void TCPSocketHandler::tls_output_fn(const Botan::byte* data, size_t size) void TCPSocketHandler::tls_alert_cb(Botan::TLS::Alert alert, const Botan::byte*, size_t) { - log_debug("tls_alert: " << alert.type_string()); + log_debug("tls_alert: ", alert.type_string()); } bool TCPSocketHandler::tls_handshake_cb(const Botan::TLS::Session& session) { - log_debug("Handshake with " << session.server_info().hostname() << " complete." - << " Version: " << session.version().to_string() - << " using " << session.ciphersuite().to_string()); + log_debug("Handshake with ", session.server_info().hostname(), " complete.", + " Version: ", session.version().to_string(), + " using ", session.ciphersuite().to_string()); if (!session.session_id().empty()) - log_debug("Session ID " << Botan::hex_encode(session.session_id())); + log_debug("Session ID ", Botan::hex_encode(session.session_id())); if (!session.session_ticket().empty()) - log_debug("Session ticket " << Botan::hex_encode(session.session_ticket())); + log_debug("Session ticket ", Botan::hex_encode(session.session_ticket())); return true; } diff --git a/louloulibs/xmpp/adhoc_commands_handler.cpp b/louloulibs/xmpp/adhoc_commands_handler.cpp index 714c440..17c4e67 100644 --- a/louloulibs/xmpp/adhoc_commands_handler.cpp +++ b/louloulibs/xmpp/adhoc_commands_handler.cpp @@ -119,5 +119,5 @@ void AdhocCommandsHandler::remove_session(const std::string& session_id, const s this->sessions.erase(session_it); return ; } - log_error("Tried to remove ad-hoc session for [" << session_id << ", " << initiator_jid << "] but none found"); + log_error("Tried to remove ad-hoc session for [", session_id, ", ", initiator_jid, "] but none found"); } diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index b92d9a3..1be7a06 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -72,14 +72,14 @@ bool XmppComponent::is_document_open() const void XmppComponent::send_stanza(const Stanza& stanza) { std::string str = stanza.to_string(); - log_debug("XMPP SENDING: " << str); + log_debug("XMPP SENDING: ", str); this->send_data(std::move(str)); } void XmppComponent::on_connection_failed(const std::string& reason) { this->first_connection_try = false; - log_error("Failed to connect to the XMPP server: " << reason); + log_error("Failed to connect to the XMPP server: ", reason); #ifdef SYSTEMD_FOUND sd_notifyf(0, "STATUS=Failed to connect to the XMPP server: %s", reason.data()); #endif @@ -91,7 +91,7 @@ void XmppComponent::on_connected() this->first_connection_try = true; auto data = ""; - log_debug("XMPP SENDING: " << data); + log_debug("XMPP SENDING: ", data); this->send_data(std::move(data)); this->doc_open = true; // We may have some pending data to send: this happens when we try to send @@ -104,7 +104,7 @@ void XmppComponent::on_connection_close(const std::string& error) if (error.empty()) log_info("XMPP server closed connection"); else - log_info("XMPP server closed connection: " << error); + log_info("XMPP server closed connection: ", error); } void XmppComponent::parse_in_buffer(const size_t size) @@ -125,7 +125,7 @@ void XmppComponent::parse_in_buffer(const size_t size) void XmppComponent::on_remote_stream_open(const XmlNode& node) { - log_debug("XMPP RECEIVING: " << node.to_string()); + log_debug("XMPP RECEIVING: ", node.to_string()); this->stream_id = node.get_tag("id"); if (this->stream_id.empty()) { @@ -147,13 +147,13 @@ void XmppComponent::on_remote_stream_open(const XmlNode& node) digest[HASH_LENGTH * 2] = '\0'; auto data = ""s + digest + ""; - log_debug("XMPP SENDING: " << data); + log_debug("XMPP SENDING: ", data); this->send_data(std::move(data)); } void XmppComponent::on_remote_stream_close(const XmlNode& node) { - log_debug("XMPP RECEIVING: " << node.to_string()); + log_debug("XMPP RECEIVING: ", node.to_string()); this->doc_open = false; } @@ -164,7 +164,7 @@ void XmppComponent::reset() void XmppComponent::on_stanza(const Stanza& stanza) { - log_debug("XMPP RECEIVING: " << stanza.to_string()); + log_debug("XMPP RECEIVING: ", stanza.to_string()); std::function handler; try { @@ -172,7 +172,7 @@ void XmppComponent::on_stanza(const Stanza& stanza) } catch (const std::out_of_range& exception) { - log_warning("No handler for stanza of type " << stanza.get_name()); + log_warning("No handler for stanza of type ", stanza.get_name()); return; } handler(stanza); @@ -257,7 +257,7 @@ void XmppComponent::handle_error(const Stanza& stanza) std::string error_message("Unspecified error"); if (text) error_message = text->get_inner(); - log_error("Stream error received from the XMPP server: " << error_message); + log_error("Stream error received from the XMPP server: ", error_message); #ifdef SYSTEMD_FOUND if (!this->ever_auth) sd_notifyf(0, "STATUS=Failed to authenticate to the XMPP server: %s", error_message.data()); diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp index dc12000..0488be9 100644 --- a/louloulibs/xmpp/xmpp_parser.cpp +++ b/louloulibs/xmpp/xmpp_parser.cpp @@ -56,7 +56,7 @@ int XmppParser::feed(const char* data, const int len, const bool is_final) int res = XML_Parse(this->parser, data, len, is_final); if (res == XML_STATUS_ERROR && (XML_GetErrorCode(this->parser) != XML_ERROR_FINISHED)) - log_error("Xml_Parse encountered an error: " << + log_error("Xml_Parse encountered an error: ", XML_ErrorString(XML_GetErrorCode(this->parser))); return res; } @@ -65,7 +65,7 @@ int XmppParser::parse(const int len, const bool is_final) { int res = XML_ParseBuffer(this->parser, len, is_final); if (res == XML_STATUS_ERROR) - log_error("Xml_Parsebuffer encountered an error: " << + log_error("Xml_Parsebuffer encountered an error: ", XML_ErrorString(XML_GetErrorCode(this->parser))); return res; } @@ -139,7 +139,7 @@ void XmppParser::stanza_event(const Stanza& stanza) const try { callback(stanza); } catch (const std::exception& e) { - log_error("Unhandled exception: " << e.what()); + log_error("Unhandled exception: ", e.what()); } } } -- cgit v1.2.3 From 46ff73662cc94220c5ee962b591c8ee327de6f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 14 Jun 2016 03:02:36 +0200 Subject: Clean the Config module, use static things instead of a stupid singleton --- louloulibs/config/config.cpp | 66 +++++++++++++++----------------------------- louloulibs/config/config.hpp | 28 ++++++++----------- louloulibs/utils/reload.cpp | 4 +-- 3 files changed, 36 insertions(+), 62 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/config/config.cpp b/louloulibs/config/config.cpp index c785632..967f581 100644 --- a/louloulibs/config/config.cpp +++ b/louloulibs/config/config.cpp @@ -6,22 +6,21 @@ #include std::string Config::filename{}; -bool Config::file_must_exist = false; +std::map Config::values{}; +std::vector Config::callbacks{}; std::string Config::get(const std::string& option, const std::string& def) { - Config* self = Config::instance().get(); - auto it = self->values.find(option); + auto it = Config::values.find(option); - if (it == self->values.end()) + if (it == Config::values.end()) return def; return it->second; } int Config::get_int(const std::string& option, const int& def) { - Config* self = Config::instance().get(); - std::string res = self->get(option, ""); + std::string res = Config::get(option, ""); if (!res.empty()) return atoi(res.c_str()); else @@ -30,65 +29,48 @@ int Config::get_int(const std::string& option, const int& def) void Config::set(const std::string& option, const std::string& value, bool save) { - Config* self = Config::instance().get(); - self->values[option] = value; + Config::values[option] = value; if (save) { - self->save_to_file(); - self->trigger_configuration_change(); + Config::save_to_file(); + Config::trigger_configuration_change(); } } void Config::connect(t_config_changed_callback callback) { - Config* self = Config::instance().get(); - self->callbacks.push_back(callback); + Config::callbacks.push_back(callback); } -void Config::close() +void Config::clear() { - Config* self = Config::instance().get(); - self->values.clear(); - Config::instance().reset(); + Config::values.clear(); } /** * Private methods */ - void Config::trigger_configuration_change() { std::vector::iterator it; - for (it = this->callbacks.begin(); it < this->callbacks.end(); ++it) + for (it = Config::callbacks.begin(); it < Config::callbacks.end(); ++it) (*it)(); } -std::unique_ptr& Config::instance() +bool Config::read_conf(const std::string& name) { - static std::unique_ptr instance; + if (!name.empty()) + Config::filename = name; - if (!instance) - { - instance = std::make_unique(); - instance->read_conf(); - } - return instance; -} - -bool Config::read_conf() -{ - std::ifstream file; - file.open(filename.data()); + std::ifstream file(Config::filename.data()); if (!file.is_open()) { - if (Config::file_must_exist) - { - perror(("Error while opening file " + filename + " for reading.").c_str()); - file.exceptions(std::ifstream::failbit); - } + perror(("Error while opening file " + filename + " for reading.").c_str()); return false; } + Config::clear(); + std::string line; size_t pos; std::string option; @@ -103,20 +85,18 @@ bool Config::read_conf() continue ; option = line.substr(0, pos); value = line.substr(pos+1); - this->values[option] = value; + Config::values[option] = value; } - return true; } -void Config::save_to_file() const +void Config::save_to_file() { - std::ofstream file(this->filename.data()); + std::ofstream file(Config::filename.data()); if (file.fail()) { std::cerr << "Could not save config file." << std::endl; return ; } - for (auto& it: this->values) + for (const auto& it: Config::values) file << it.first << "=" << it.second << '\n'; - file.close(); } diff --git a/louloulibs/config/config.hpp b/louloulibs/config/config.hpp index 72620c0..b46768e 100644 --- a/louloulibs/config/config.hpp +++ b/louloulibs/config/config.hpp @@ -60,38 +60,34 @@ public: * Destroy the instance, forcing it to be recreated (with potentially * different parameters) the next time it’s needed. */ - static void close(); + static void clear(); /** - * Set the value of the filename to use, before calling any method. + * Read the configuration file at the given path. */ - static std::string filename; + static bool read_conf(const std::string& name=""); /** - * Set to true if you want an exception to be raised if the file does not - * exist when reading it. + * Get the filename */ - static bool file_must_exist; + static const std::string& get_filename() + { return Config::filename; } private: /** - * Get the singleton instance - */ - static std::unique_ptr& instance(); - /** - * Read the configuration file at the given path. + * Set the value of the filename to use, before calling any method. */ - bool read_conf(); + static std::string filename; /** * Write all the config values into the configuration file */ - void save_to_file() const; + static void save_to_file(); /** * Call all the callbacks previously registered using connect(). * This is used to notify any class that a configuration change occured. */ - void trigger_configuration_change(); + static void trigger_configuration_change(); - std::map values; - std::vector callbacks; + static std::map values; + static std::vector callbacks; }; diff --git a/louloulibs/utils/reload.cpp b/louloulibs/utils/reload.cpp index 6600c75..bddfd86 100644 --- a/louloulibs/utils/reload.cpp +++ b/louloulibs/utils/reload.cpp @@ -3,9 +3,7 @@ void reload_process() { - // Closing the config will just force it to be reopened the next time - // a configuration option is needed - Config::close(); + Config::read_conf(); // Destroy the logger instance, to be recreated the next time a log // line needs to be written Logger::instance().reset(); -- cgit v1.2.3 From 19bca5c2f2b104d534a7c8be7f61dc48d928cf24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 14 Jun 2016 20:08:55 +0200 Subject: Add a missing return bool in Config --- louloulibs/config/config.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'louloulibs') diff --git a/louloulibs/config/config.cpp b/louloulibs/config/config.cpp index 967f581..8027d20 100644 --- a/louloulibs/config/config.cpp +++ b/louloulibs/config/config.cpp @@ -87,6 +87,7 @@ bool Config::read_conf(const std::string& name) value = line.substr(pos+1); Config::values[option] = value; } + return true; } void Config::save_to_file() -- cgit v1.2.3 From 80d0c19c5a8d548a8c6019033bf574ff2be4c0ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 14 Jun 2016 20:14:36 +0200 Subject: Refactor, test and improve the way we cut too-long messages for IRC --- louloulibs/utils/string.cpp | 12 ++++++++++++ louloulibs/utils/string.hpp | 2 ++ 2 files changed, 14 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/utils/string.cpp b/louloulibs/utils/string.cpp index 3977feb..7ed1aa3 100644 --- a/louloulibs/utils/string.cpp +++ b/louloulibs/utils/string.cpp @@ -4,3 +4,15 @@ bool to_bool(const std::string& val) { return (val == "1" || val == "true"); } + +std::vector cut(const std::string& val, const std::size_t size) +{ + std::vector res; + std::string::size_type pos = 0; + while (pos < val.size()) + { + res.emplace_back(val.substr(pos, size)); + pos += size; + } + return res; +} diff --git a/louloulibs/utils/string.hpp b/louloulibs/utils/string.hpp index 3775c36..1c8f001 100644 --- a/louloulibs/utils/string.hpp +++ b/louloulibs/utils/string.hpp @@ -1,8 +1,10 @@ #ifndef STRING_UTILS_HPP_INCLUDED #define STRING_UTILS_HPP_INCLUDED +#include #include bool to_bool(const std::string& val); +std::vector cut(const std::string& val, const std::size_t size); #endif /* STRING_UTILS_HPP_INCLUDED */ -- cgit v1.2.3 From 6235fb2d0326b18a9e013ae13dfb1fd0577ffd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 15 Jun 2016 00:38:43 +0200 Subject: Add get_next_codepoint_size --- louloulibs/utils/encoding.cpp | 28 ++++++++++++++++------------ louloulibs/utils/encoding.hpp | 5 +++++ 2 files changed, 21 insertions(+), 12 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/utils/encoding.cpp b/louloulibs/utils/encoding.cpp index f738ce2..507f38a 100644 --- a/louloulibs/utils/encoding.cpp +++ b/louloulibs/utils/encoding.cpp @@ -23,6 +23,17 @@ namespace utils /** * Based on http://en.wikipedia.org/wiki/UTF-8#Description */ + std::size_t get_next_codepoint_size(const unsigned char c) + { + if ((c & 0b11111000) == 0b11110000) // 4 bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + return 4; + else if ((c & 0b11110000) == 0b11100000) // 3 bytes: 1110xxx 10xxxxxx 10xxxxxx + return 3; + else if ((c & 0b11100000) == 0b11000000) // 2 bytes: 110xxxxx 10xxxxxx + return 2; + return 1; // 1 byte: 0xxxxxxx + } + bool is_valid_utf8(const char* s) { if (!s) @@ -32,38 +43,31 @@ namespace utils while (*str) { - // 4 bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - if ((str[0] & 0b11111000) == 0b11110000) + const auto codepoint_size = get_next_codepoint_size(str[0]); + if (codepoint_size == 4) { if (!str[1] || !str[2] || !str[3] || ((str[1] & 0b11000000) != 0b10000000) || ((str[2] & 0b11000000) != 0b10000000) || ((str[3] & 0b11000000) != 0b10000000)) return false; - str += 4; } - // 3 bytes: 1110xxx 10xxxxxx 10xxxxxx - else if ((str[0] & 0b11110000) == 0b11100000) + else if (codepoint_size == 3) { if (!str[1] || !str[2] || ((str[1] & 0b11000000) != 0b10000000) || ((str[2] & 0b11000000) != 0b10000000)) return false; - str += 3; } - // 2 bytes: 110xxxxx 10xxxxxx - else if (((str[0]) & 0b11100000) == 0b11000000) + else if (codepoint_size == 2) { if (!str[1] || ((str[1] & 0b11000000) != 0b10000000)) return false; - str += 2; } - // 1 byte: 0xxxxxxx else if ((str[0] & 0b10000000) != 0) return false; - else - str++; + str += codepoint_size; } return true; } diff --git a/louloulibs/utils/encoding.hpp b/louloulibs/utils/encoding.hpp index 6b7ccd2..3f55055 100644 --- a/louloulibs/utils/encoding.hpp +++ b/louloulibs/utils/encoding.hpp @@ -5,6 +5,11 @@ namespace utils { + /** + * Return the size, in bytes, of the next UTF-8 codepoint, based on + * the given char. + */ + std::size_t get_next_codepoint_size(const unsigned char c); /** * Returns true if the given null-terminated string is valid utf-8. * -- cgit v1.2.3 From 4b1c580bb9bc03d656e59d702c72c3e793a1bbe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 15 Jun 2016 12:19:19 +0200 Subject: cut messages at 512 bytes, taking into account the UTF-8 codepoints ref #3067 --- louloulibs/utils/string.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/utils/string.cpp b/louloulibs/utils/string.cpp index 7ed1aa3..2447f84 100644 --- a/louloulibs/utils/string.cpp +++ b/louloulibs/utils/string.cpp @@ -1,4 +1,5 @@ #include +#include bool to_bool(const std::string& val) { @@ -11,8 +12,17 @@ std::vector cut(const std::string& val, const std::size_t size) std::string::size_type pos = 0; while (pos < val.size()) { - res.emplace_back(val.substr(pos, size)); - pos += size; + // Get the number of chars, <= size, that contain only whole + // UTF-8 codepoints. + std::size_t s = 0; + auto codepoint_size = utils::get_next_codepoint_size(val[pos + s]); + while (s + codepoint_size <= size) + { + s += codepoint_size; + codepoint_size = utils::get_next_codepoint_size(val[pos + s]); + } + res.emplace_back(val.substr(pos, s)); + pos += s; } return res; } -- cgit v1.2.3 From 9206f4723b8c5feb8a96daa9c283d30cf73290e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 23 Jun 2016 22:40:53 +0200 Subject: Fix a bug in cut() --- louloulibs/utils/string.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/utils/string.cpp b/louloulibs/utils/string.cpp index 2447f84..635e71a 100644 --- a/louloulibs/utils/string.cpp +++ b/louloulibs/utils/string.cpp @@ -16,7 +16,7 @@ std::vector cut(const std::string& val, const std::size_t size) // UTF-8 codepoints. std::size_t s = 0; auto codepoint_size = utils::get_next_codepoint_size(val[pos + s]); - while (s + codepoint_size <= size) + while (s + codepoint_size <= size && pos + s < val.size()) { s += codepoint_size; codepoint_size = utils::get_next_codepoint_size(val[pos + s]); -- cgit v1.2.3 From b2e7edeea8bf08b6b7e75d60af3af0c30fdaa4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 24 Jun 2016 11:23:01 +0200 Subject: =?UTF-8?q?Properly=20set=20the=20=E2=80=9Cfrom=E2=80=9D=20of=20th?= =?UTF-8?q?e=20ping=20results=20to=20the=20correct=20full=20JID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- louloulibs/xmpp/xmpp_component.cpp | 10 ++++++++++ louloulibs/xmpp/xmpp_component.hpp | 2 ++ 2 files changed, 12 insertions(+) (limited to 'louloulibs') diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp index 1be7a06..e87cdf7 100644 --- a/louloulibs/xmpp/xmpp_component.cpp +++ b/louloulibs/xmpp/xmpp_component.cpp @@ -631,6 +631,16 @@ void XmppComponent::send_iq_version_request(const std::string& from, this->send_stanza(iq); } +void XmppComponent::send_iq_result_full_jid(const std::string& id, const std::string& to_jid, const std::string& from_full_jid) +{ + Stanza iq("iq"); + iq["from"] = from_full_jid; + iq["to"] = to_jid; + iq["id"] = id; + iq["type"] = "result"; + this->send_stanza(iq); +} + void XmppComponent::send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from_local_part) { Stanza iq("iq"); diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp index 913e337..ae4d76b 100644 --- a/louloulibs/xmpp/xmpp_component.hpp +++ b/louloulibs/xmpp/xmpp_component.hpp @@ -200,6 +200,8 @@ public: * Send an empty iq of type result */ void send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from); + void send_iq_result_full_jid(const std::string& id, const std::string& to_jid, + const std::string& from_full_jid); void handle_handshake(const Stanza& stanza); void handle_error(const Stanza& stanza); -- cgit v1.2.3 From 81f8f45b371d1a0ef72c2768fbd1f9188fe83616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 4 Jul 2016 17:53:53 +0200 Subject: Replace all include guards by #pragma once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s $CURRENT_YEAR --- louloulibs/config/config.hpp | 6 +++--- louloulibs/logger/logger.hpp | 6 +++--- louloulibs/network/credentials_manager.hpp | 5 ++--- louloulibs/network/dns_handler.hpp | 4 +--- louloulibs/network/dns_socket_handler.hpp | 4 +--- louloulibs/network/poller.hpp | 6 +++--- louloulibs/network/resolver.hpp | 6 +++--- louloulibs/network/socket_handler.hpp | 4 +--- louloulibs/network/tcp_socket_handler.hpp | 6 +++--- louloulibs/utils/encoding.hpp | 6 +++--- louloulibs/utils/reload.hpp | 6 +++--- louloulibs/utils/revstr.hpp | 6 +++--- louloulibs/utils/scopeguard.hpp | 4 +--- louloulibs/utils/split.hpp | 6 +++--- louloulibs/utils/string.hpp | 6 +++--- louloulibs/utils/timed_events.hpp | 5 +---- louloulibs/utils/tolower.hpp | 6 +++--- louloulibs/utils/xdg.hpp | 6 +++--- louloulibs/xmpp/adhoc_command.hpp | 5 +---- louloulibs/xmpp/adhoc_commands_handler.hpp | 5 +---- louloulibs/xmpp/adhoc_session.hpp | 5 +---- louloulibs/xmpp/body.hpp | 6 +++--- louloulibs/xmpp/jid.hpp | 6 +++--- louloulibs/xmpp/roster.hpp | 6 +++--- louloulibs/xmpp/xmpp_component.hpp | 6 +++--- louloulibs/xmpp/xmpp_parser.hpp | 6 +++--- louloulibs/xmpp/xmpp_stanza.hpp | 6 +++--- 27 files changed, 64 insertions(+), 85 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/config/config.hpp b/louloulibs/config/config.hpp index b46768e..6728df8 100644 --- a/louloulibs/config/config.hpp +++ b/louloulibs/config/config.hpp @@ -13,8 +13,8 @@ * save the config into the file. */ -#ifndef CONFIG_INCLUDED -# define CONFIG_INCLUDED +#pragma once + #include #include @@ -91,4 +91,4 @@ private: }; -#endif // CONFIG_INCLUDED + diff --git a/louloulibs/logger/logger.hpp b/louloulibs/logger/logger.hpp index 8ff4dcd..0893c77 100644 --- a/louloulibs/logger/logger.hpp +++ b/louloulibs/logger/logger.hpp @@ -1,5 +1,5 @@ -#ifndef LOGGER_INCLUDED -# define LOGGER_INCLUDED +#pragma once + /** * Singleton used in logger macros to write into files or stdout, with @@ -123,4 +123,4 @@ namespace logging_details #define log_debug(...) logging_details::log_debug(WHERE, __VA_ARGS__) -#endif // LOGGER_INCLUDED + diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp index c0d23ae..0fc4b89 100644 --- a/louloulibs/network/credentials_manager.hpp +++ b/louloulibs/network/credentials_manager.hpp @@ -1,5 +1,4 @@ -#ifndef BIBOUMI_CREDENTIALS_MANAGER_HPP -#define BIBOUMI_CREDENTIALS_MANAGER_HPP +#pragma once #include "louloulibs.h" @@ -37,4 +36,4 @@ private: }; #endif //BOTAN_FOUND -#endif //BIBOUMI_CREDENTIALS_MANAGER_HPP + diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index d2b48d2..e0feb11 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -1,5 +1,4 @@ -#ifndef DNS_HANDLER_HPP_INCLUDED -#define DNS_HANDLER_HPP_INCLUDED +#pragma once #include #ifdef CARES_FOUND @@ -56,4 +55,3 @@ private: }; #endif /* CARES_FOUND */ -#endif /* DNS_HANDLER_HPP_INCLUDED */ diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index 5ea9846..dba2f26 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -1,5 +1,4 @@ -#ifndef DNS_SOCKET_HANDLER_HPP -# define DNS_SOCKET_HANDLER_HPP +#pragma once #include #ifdef CARES_FOUND @@ -44,4 +43,3 @@ private: }; #endif // CARES_FOUND -#endif // DNS_SOCKET_HANDLER_HPP diff --git a/louloulibs/network/poller.hpp b/louloulibs/network/poller.hpp index 4bf7432..fc1a1a1 100644 --- a/louloulibs/network/poller.hpp +++ b/louloulibs/network/poller.hpp @@ -1,5 +1,5 @@ -#ifndef POLLER_INCLUDED -# define POLLER_INCLUDED +#pragma once + #include @@ -91,4 +91,4 @@ private: #endif }; -#endif // POLLER_INCLUDED + diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp index fd57e25..afe6e2b 100644 --- a/louloulibs/network/resolver.hpp +++ b/louloulibs/network/resolver.hpp @@ -1,5 +1,5 @@ -#ifndef RESOLVER_HPP_INCLUDED -#define RESOLVER_HPP_INCLUDED +#pragma once + #include "louloulibs.h" @@ -125,4 +125,4 @@ private: std::string addr_to_string(const struct addrinfo* rp); -#endif /* RESOLVER_HPP_INCLUDED */ + diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index feabcf1..eeb41fe 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -1,5 +1,4 @@ -#ifndef SOCKET_HANDLER_HPP -# define SOCKET_HANDLER_HPP +#pragma once #include #include @@ -41,4 +40,3 @@ protected: socket_t socket; }; -#endif // SOCKET_HANDLER_HPP diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp index 91fa7a8..b0ba493 100644 --- a/louloulibs/network/tcp_socket_handler.hpp +++ b/louloulibs/network/tcp_socket_handler.hpp @@ -1,5 +1,5 @@ -#ifndef SOCKET_HANDLER_INCLUDED -# define SOCKET_HANDLER_INCLUDED +#pragma once + #include "louloulibs.h" @@ -270,5 +270,5 @@ private: #endif // BOTAN_FOUND }; -#endif // SOCKET_HANDLER_INCLUDED + diff --git a/louloulibs/utils/encoding.hpp b/louloulibs/utils/encoding.hpp index 3f55055..586edd8 100644 --- a/louloulibs/utils/encoding.hpp +++ b/louloulibs/utils/encoding.hpp @@ -1,5 +1,5 @@ -#ifndef ENCODING_INCLUDED -# define ENCODING_INCLUDED +#pragma once + #include @@ -40,4 +40,4 @@ namespace xep0106 void encode(std::string&); } -#endif // ENCODING_INCLUDED + diff --git a/louloulibs/utils/reload.hpp b/louloulibs/utils/reload.hpp index 16d64f7..6a56acd 100644 --- a/louloulibs/utils/reload.hpp +++ b/louloulibs/utils/reload.hpp @@ -1,5 +1,5 @@ -#ifndef RELOAD_HPP_INCLUDED -#define RELOAD_HPP_INCLUDED +#pragma once + /** * Reload the server's configuration, and close the logger (so that it @@ -7,4 +7,4 @@ */ void reload_process(); -#endif /* RELOAD_HPP_INCLUDED */ + diff --git a/louloulibs/utils/revstr.hpp b/louloulibs/utils/revstr.hpp index 27c9e3e..8e521ea 100644 --- a/louloulibs/utils/revstr.hpp +++ b/louloulibs/utils/revstr.hpp @@ -1,5 +1,5 @@ -#ifndef REVSTR_HPP_INCLUDED -# define REVSTR_HPP_INCLUDED +#pragma once + #include @@ -8,4 +8,4 @@ namespace utils std::string revstr(const std::string& original); } -#endif // REVSTR_HPP_INCLUDED + diff --git a/louloulibs/utils/scopeguard.hpp b/louloulibs/utils/scopeguard.hpp index fed40ff..ee1e2ef 100644 --- a/louloulibs/utils/scopeguard.hpp +++ b/louloulibs/utils/scopeguard.hpp @@ -1,5 +1,4 @@ -#ifndef SCOPEGUARD_HPP -#define SCOPEGUARD_HPP +#pragma once #include #include @@ -88,4 +87,3 @@ private: } -#endif diff --git a/louloulibs/utils/split.hpp b/louloulibs/utils/split.hpp index 6b487a9..3755ef8 100644 --- a/louloulibs/utils/split.hpp +++ b/louloulibs/utils/split.hpp @@ -1,5 +1,5 @@ -#ifndef SPLIT_INCLUDED -# define SPLIT_INCLUDED +#pragma once + #include #include @@ -9,4 +9,4 @@ namespace utils std::vector split(const std::string &s, const char delim, const bool allow_empty=true); } -#endif // SPLIT_INCLUDED + diff --git a/louloulibs/utils/string.hpp b/louloulibs/utils/string.hpp index 1c8f001..84ba101 100644 --- a/louloulibs/utils/string.hpp +++ b/louloulibs/utils/string.hpp @@ -1,5 +1,5 @@ -#ifndef STRING_UTILS_HPP_INCLUDED -#define STRING_UTILS_HPP_INCLUDED +#pragma once + #include #include @@ -7,4 +7,4 @@ bool to_bool(const std::string& val); std::vector cut(const std::string& val, const std::size_t size); -#endif /* STRING_UTILS_HPP_INCLUDED */ + diff --git a/louloulibs/utils/timed_events.hpp b/louloulibs/utils/timed_events.hpp index 70e2eff..6e28206 100644 --- a/louloulibs/utils/timed_events.hpp +++ b/louloulibs/utils/timed_events.hpp @@ -1,5 +1,4 @@ -#ifndef TIMED_EVENTS_HPP -# define TIMED_EVENTS_HPP +#pragma once #include #include @@ -131,5 +130,3 @@ private: std::vector events; explicit TimedEventsManager() = default; }; - -#endif // TIMED_EVENTS_HPP diff --git a/louloulibs/utils/tolower.hpp b/louloulibs/utils/tolower.hpp index 0019182..650e05d 100644 --- a/louloulibs/utils/tolower.hpp +++ b/louloulibs/utils/tolower.hpp @@ -1,5 +1,5 @@ -#ifndef TOLOWER_INCLUDED -# define TOLOWER_INCLUDED +#pragma once + #include @@ -8,4 +8,4 @@ namespace utils std::string tolower(const std::string& original); } -#endif // SPLIT_INCLUDED + diff --git a/louloulibs/utils/xdg.hpp b/louloulibs/utils/xdg.hpp index 15f3d0b..56e11da 100644 --- a/louloulibs/utils/xdg.hpp +++ b/louloulibs/utils/xdg.hpp @@ -1,5 +1,5 @@ -#ifndef XDG_HPP_INCLUDED -#define XDG_HPP_INCLUDED +#pragma once + #include @@ -11,4 +11,4 @@ std::string xdg_config_path(const std::string& filename); std::string xdg_data_path(const std::string& filename); -#endif /* XDG_HPP_INCLUDED */ + diff --git a/louloulibs/xmpp/adhoc_command.hpp b/louloulibs/xmpp/adhoc_command.hpp index 1c4e4de..7c4de47 100644 --- a/louloulibs/xmpp/adhoc_command.hpp +++ b/louloulibs/xmpp/adhoc_command.hpp @@ -1,5 +1,4 @@ -#ifndef ADHOC_COMMAND_HPP -# define ADHOC_COMMAND_HPP +#pragma once /** * Describe an ad-hoc command. @@ -43,5 +42,3 @@ void PingStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node); void HelloStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node); void HelloStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node); void Reload(XmppComponent&, AdhocSession& session, XmlNode& command_node); - -#endif // ADHOC_COMMAND_HPP diff --git a/louloulibs/xmpp/adhoc_commands_handler.hpp b/louloulibs/xmpp/adhoc_commands_handler.hpp index 65b094d..91eb5bd 100644 --- a/louloulibs/xmpp/adhoc_commands_handler.hpp +++ b/louloulibs/xmpp/adhoc_commands_handler.hpp @@ -1,5 +1,4 @@ -#ifndef ADHOC_COMMANDS_HANDLER_HPP -# define ADHOC_COMMANDS_HANDLER_HPP +#pragma once /** * Manage a list of available AdhocCommands and the list of ongoing @@ -70,5 +69,3 @@ private: */ std::map, AdhocSession> sessions; }; - -#endif // ADHOC_COMMANDS_HANDLER_HPP diff --git a/louloulibs/xmpp/adhoc_session.hpp b/louloulibs/xmpp/adhoc_session.hpp index 0966c4e..0de8d13 100644 --- a/louloulibs/xmpp/adhoc_session.hpp +++ b/louloulibs/xmpp/adhoc_session.hpp @@ -1,5 +1,4 @@ -#ifndef ADHOC_SESSION_HPP -# define ADHOC_SESSION_HPP +#pragma once #include @@ -87,5 +86,3 @@ public: */ std::map vars; }; - -#endif // ADHOC_SESSION_HPP diff --git a/louloulibs/xmpp/body.hpp b/louloulibs/xmpp/body.hpp index df86feb..068d1a4 100644 --- a/louloulibs/xmpp/body.hpp +++ b/louloulibs/xmpp/body.hpp @@ -1,5 +1,5 @@ -#ifndef XMPP_BODY_HPP_INCLUDED -#define XMPP_BODY_HPP_INCLUDED +#pragma once + namespace Xmpp { @@ -9,4 +9,4 @@ namespace Xmpp using body = std::tuple>; } -#endif /* XMPP_BODY_HPP_INCLUDED */ + diff --git a/louloulibs/xmpp/jid.hpp b/louloulibs/xmpp/jid.hpp index 5ff5a4f..08327ef 100644 --- a/louloulibs/xmpp/jid.hpp +++ b/louloulibs/xmpp/jid.hpp @@ -1,5 +1,5 @@ -#ifndef JID_INCLUDED -# define JID_INCLUDED +#pragma once + #include @@ -41,4 +41,4 @@ public: */ std::string jidprep(const std::string& original); -#endif // JID_INCLUDED + diff --git a/louloulibs/xmpp/roster.hpp b/louloulibs/xmpp/roster.hpp index 0aebca5..aa1b449 100644 --- a/louloulibs/xmpp/roster.hpp +++ b/louloulibs/xmpp/roster.hpp @@ -1,5 +1,5 @@ -#ifndef ROSTER_HPP_INCLUDED -#define ROSTER_HPP_INCLUDED +#pragma once + #include #include @@ -68,4 +68,4 @@ private: Roster& operator=(Roster&&) = delete; }; -#endif /* ROSTER_HPP_INCLUDED */ + diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp index ae4d76b..5fc6d2e 100644 --- a/louloulibs/xmpp/xmpp_component.hpp +++ b/louloulibs/xmpp/xmpp_component.hpp @@ -1,5 +1,5 @@ -#ifndef XMPP_COMPONENT_INCLUDED -# define XMPP_COMPONENT_INCLUDED +#pragma once + #include #include @@ -240,4 +240,4 @@ protected: AdhocCommandsHandler adhoc_commands_handler; }; -#endif // XMPP_COMPONENT_INCLUDED + diff --git a/louloulibs/xmpp/xmpp_parser.hpp b/louloulibs/xmpp/xmpp_parser.hpp index 93c6c53..9d67228 100644 --- a/louloulibs/xmpp/xmpp_parser.hpp +++ b/louloulibs/xmpp/xmpp_parser.hpp @@ -1,5 +1,5 @@ -#ifndef XMPP_PARSER_INCLUDED -# define XMPP_PARSER_INCLUDED +#pragma once + #include @@ -130,4 +130,4 @@ private: std::vector> stream_close_callbacks; }; -#endif // XMPP_PARSER_INCLUDED + diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp index 28b8414..4ca758e 100644 --- a/louloulibs/xmpp/xmpp_stanza.hpp +++ b/louloulibs/xmpp/xmpp_stanza.hpp @@ -1,5 +1,5 @@ -#ifndef XMPP_STANZA_INCLUDED -# define XMPP_STANZA_INCLUDED +#pragma once + #include #include @@ -143,4 +143,4 @@ std::ostream& operator<<(std::ostream& os, const XmlNode& node); */ using Stanza = XmlNode; -#endif // XMPP_STANZA_INCLUDED + -- cgit v1.2.3 From 4c1b9abe7e230a39b119bdc45ebcd5e677fad488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 12 Jul 2016 00:31:57 +0200 Subject: Properly catch and handle database errors Do not use a singleton for the database. fix #3203 --- louloulibs/utils/reload.cpp | 19 ++++++++++++++++++- louloulibs/utils/reload.hpp | 8 +------- 2 files changed, 19 insertions(+), 8 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/utils/reload.cpp b/louloulibs/utils/reload.cpp index bddfd86..7125a75 100644 --- a/louloulibs/utils/reload.cpp +++ b/louloulibs/utils/reload.cpp @@ -1,11 +1,28 @@ +#include +#include #include +#include #include +void open_database() +{ + const auto db_filename = Config::get("db_name", xdg_data_path("biboumi.sqlite")); + log_info("Opening database: ", db_filename); + Database::open(db_filename); + log_info("database successfully opened."); +} + void reload_process() { Config::read_conf(); // Destroy the logger instance, to be recreated the next time a log // line needs to be written Logger::instance().reset(); - log_debug("Configuration and logger reloaded."); + log_info("Configuration and logger reloaded."); + try { + open_database(); + } catch (const litesql::DatabaseError&) { + log_warning("Re-using the previous database."); + } } + diff --git a/louloulibs/utils/reload.hpp b/louloulibs/utils/reload.hpp index 6a56acd..408426a 100644 --- a/louloulibs/utils/reload.hpp +++ b/louloulibs/utils/reload.hpp @@ -1,10 +1,4 @@ #pragma once - -/** - * Reload the server's configuration, and close the logger (so that it - * closes its files etc, to take into account the new configuration) - */ +void open_database(); void reload_process(); - - -- cgit v1.2.3 From 9fb2e116c47a9d4e2866d34450d12dcb90d4a26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 12 Jul 2016 01:15:34 +0200 Subject: Move reload.*pp from louloulibs to src --- louloulibs/utils/reload.cpp | 28 ---------------------------- louloulibs/utils/reload.hpp | 4 ---- 2 files changed, 32 deletions(-) delete mode 100644 louloulibs/utils/reload.cpp delete mode 100644 louloulibs/utils/reload.hpp (limited to 'louloulibs') diff --git a/louloulibs/utils/reload.cpp b/louloulibs/utils/reload.cpp deleted file mode 100644 index 7125a75..0000000 --- a/louloulibs/utils/reload.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include -#include -#include -#include - -void open_database() -{ - const auto db_filename = Config::get("db_name", xdg_data_path("biboumi.sqlite")); - log_info("Opening database: ", db_filename); - Database::open(db_filename); - log_info("database successfully opened."); -} - -void reload_process() -{ - Config::read_conf(); - // Destroy the logger instance, to be recreated the next time a log - // line needs to be written - Logger::instance().reset(); - log_info("Configuration and logger reloaded."); - try { - open_database(); - } catch (const litesql::DatabaseError&) { - log_warning("Re-using the previous database."); - } -} - diff --git a/louloulibs/utils/reload.hpp b/louloulibs/utils/reload.hpp deleted file mode 100644 index 408426a..0000000 --- a/louloulibs/utils/reload.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -void open_database(); -void reload_process(); -- cgit v1.2.3 From 5328d0806fdc5becb9344b4d4320787a2b7c0712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 7 Jul 2016 09:27:22 +0200 Subject: =?UTF-8?q?Don=E2=80=99t=20use=20unique=5Fptr=20to=20store=20dns?= =?UTF-8?q?=20socket=20handlers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- louloulibs/network/dns_handler.cpp | 14 +++++++------- louloulibs/network/dns_handler.hpp | 2 +- louloulibs/network/dns_socket_handler.hpp | 6 +++--- louloulibs/network/socket_handler.hpp | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index 5e19f8c..24c4bcf 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -68,7 +68,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) std::remove_if(this->socket_handlers.begin(), this->socket_handlers.end(), [&readers](const auto& dns_socket) { - return !FD_ISSET(dns_socket->get_socket(), &readers); + return !FD_ISSET(dns_socket.get_socket(), &readers); }), this->socket_handlers.end()); @@ -81,8 +81,8 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) this->socket_handlers.end(), [i](const auto& socket_handler) { - return i == socket_handler->get_socket(); - }); + return i == socket_handler.get_socket(); + }); if (!read && !write) // No need to read or write to it { // If found, erase it and stop watching it because it is not // needed anymore @@ -95,12 +95,12 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) if (it == this->socket_handlers.end()) { this->socket_handlers.emplace(this->socket_handlers.begin(), - std::make_unique(poller, i)); + poller, i); it = this->socket_handlers.begin(); } - poller->add_socket_handler(it->get()); + poller->add_socket_handler(&*it); if (write) - poller->watch_send_events(it->get()); + poller->watch_send_events(&*it); } } // Cancel previous timer, if any. @@ -116,7 +116,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) [this]() { for (auto& dns_socket_handler: this->socket_handlers) - dns_socket_handler->on_recv(); + dns_socket_handler.on_recv(); }, "DNS timeout")); } diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index e0feb11..d8b55c8 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -50,7 +50,7 @@ private: * call to ares_fds. DNSSocketHandlers are added to it or removed from it * in the watch_dns_sockets() method */ - std::vector> socket_handlers; + std::vector socket_handlers; ares_channel channel; }; diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index dba2f26..21ae4e1 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -17,11 +17,11 @@ class DNSSocketHandler: public SocketHandler { public: explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); - ~DNSSocketHandler() = default; + ~DNSSocketHandler(); + DNSSocketHandler(DNSSocketHandler&&) = default; + DNSSocketHandler& operator=(DNSSocketHandler&&) = default; DNSSocketHandler(const DNSSocketHandler&) = delete; - DNSSocketHandler(DNSSocketHandler&&) = delete; DNSSocketHandler& operator=(const DNSSocketHandler&) = delete; - DNSSocketHandler& operator=(DNSSocketHandler&&) = delete; /** * Just call dns_process_fd, c-ares will do its work of send()ing or diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index eeb41fe..869ac3b 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -15,10 +15,10 @@ public: socket(socket) {} virtual ~SocketHandler() {} - SocketHandler(const SocketHandler&) = delete; - SocketHandler(SocketHandler&&) = delete; + SocketHandler& operator=(SocketHandler&&) = default; + SocketHandler(SocketHandler&&) = default; SocketHandler& operator=(const SocketHandler&) = delete; - SocketHandler& operator=(SocketHandler&&) = delete; + SocketHandler(const SocketHandler&) = delete; virtual void on_recv() = 0; virtual void on_send() = 0; -- cgit v1.2.3 From fa42d0c178faa74b6872c4e0121709ef6682175d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 12 Jul 2016 11:25:38 +0200 Subject: Bring back DNSSocketHandler's destructor --- louloulibs/network/dns_socket_handler.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index 21ae4e1..bd431e1 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -17,7 +17,7 @@ class DNSSocketHandler: public SocketHandler { public: explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); - ~DNSSocketHandler(); + ~DNSSocketHandler() = default; DNSSocketHandler(DNSSocketHandler&&) = default; DNSSocketHandler& operator=(DNSSocketHandler&&) = default; DNSSocketHandler(const DNSSocketHandler&) = delete; -- cgit v1.2.3 From db503b23e86d1cb390d12298875eb0eaffdbfa3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 24 Jul 2016 19:41:01 +0200 Subject: Use log_error instead of cerr --- louloulibs/config/config.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/config/config.cpp b/louloulibs/config/config.cpp index 8027d20..417981d 100644 --- a/louloulibs/config/config.cpp +++ b/louloulibs/config/config.cpp @@ -1,9 +1,10 @@ #include +#include -#include +#include #include -#include +#include std::string Config::filename{}; std::map Config::values{}; @@ -22,7 +23,7 @@ int Config::get_int(const std::string& option, const int& def) { std::string res = Config::get(option, ""); if (!res.empty()) - return atoi(res.c_str()); + return std::atoi(res.c_str()); else return def; } @@ -65,7 +66,7 @@ bool Config::read_conf(const std::string& name) std::ifstream file(Config::filename.data()); if (!file.is_open()) { - perror(("Error while opening file " + filename + " for reading.").c_str()); + log_error("Error while opening file ", filename, " for reading: ", strerror(errno)); return false; } @@ -95,7 +96,7 @@ void Config::save_to_file() std::ofstream file(Config::filename.data()); if (file.fail()) { - std::cerr << "Could not save config file." << std::endl; + log_error("Could not save config file."); return ; } for (const auto& it: Config::values) -- cgit v1.2.3 From de0eff6e944db11ae3552e0cd2c191997eebaa2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 27 Jul 2016 10:04:15 +0200 Subject: =?UTF-8?q?Revert=20"Don=E2=80=99t=20use=20unique=5Fptr=20to=20sto?= =?UTF-8?q?re=20dns=20socket=20handlers"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5328d0806fdc5becb9344b4d4320787a2b7c0712. --- louloulibs/network/dns_handler.cpp | 14 +++++++------- louloulibs/network/dns_handler.hpp | 2 +- louloulibs/network/dns_socket_handler.hpp | 4 ++-- louloulibs/network/socket_handler.hpp | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index 24c4bcf..5e19f8c 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -68,7 +68,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) std::remove_if(this->socket_handlers.begin(), this->socket_handlers.end(), [&readers](const auto& dns_socket) { - return !FD_ISSET(dns_socket.get_socket(), &readers); + return !FD_ISSET(dns_socket->get_socket(), &readers); }), this->socket_handlers.end()); @@ -81,8 +81,8 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) this->socket_handlers.end(), [i](const auto& socket_handler) { - return i == socket_handler.get_socket(); - }); + return i == socket_handler->get_socket(); + }); if (!read && !write) // No need to read or write to it { // If found, erase it and stop watching it because it is not // needed anymore @@ -95,12 +95,12 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) if (it == this->socket_handlers.end()) { this->socket_handlers.emplace(this->socket_handlers.begin(), - poller, i); + std::make_unique(poller, i)); it = this->socket_handlers.begin(); } - poller->add_socket_handler(&*it); + poller->add_socket_handler(it->get()); if (write) - poller->watch_send_events(&*it); + poller->watch_send_events(it->get()); } } // Cancel previous timer, if any. @@ -116,7 +116,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) [this]() { for (auto& dns_socket_handler: this->socket_handlers) - dns_socket_handler.on_recv(); + dns_socket_handler->on_recv(); }, "DNS timeout")); } diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index d8b55c8..e0feb11 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -50,7 +50,7 @@ private: * call to ares_fds. DNSSocketHandlers are added to it or removed from it * in the watch_dns_sockets() method */ - std::vector socket_handlers; + std::vector> socket_handlers; ares_channel channel; }; diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index bd431e1..dba2f26 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -18,10 +18,10 @@ class DNSSocketHandler: public SocketHandler public: explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); ~DNSSocketHandler() = default; - DNSSocketHandler(DNSSocketHandler&&) = default; - DNSSocketHandler& operator=(DNSSocketHandler&&) = default; DNSSocketHandler(const DNSSocketHandler&) = delete; + DNSSocketHandler(DNSSocketHandler&&) = delete; DNSSocketHandler& operator=(const DNSSocketHandler&) = delete; + DNSSocketHandler& operator=(DNSSocketHandler&&) = delete; /** * Just call dns_process_fd, c-ares will do its work of send()ing or diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp index 869ac3b..eeb41fe 100644 --- a/louloulibs/network/socket_handler.hpp +++ b/louloulibs/network/socket_handler.hpp @@ -15,10 +15,10 @@ public: socket(socket) {} virtual ~SocketHandler() {} - SocketHandler& operator=(SocketHandler&&) = default; - SocketHandler(SocketHandler&&) = default; - SocketHandler& operator=(const SocketHandler&) = delete; SocketHandler(const SocketHandler&) = delete; + SocketHandler(SocketHandler&&) = delete; + SocketHandler& operator=(const SocketHandler&) = delete; + SocketHandler& operator=(SocketHandler&&) = delete; virtual void on_recv() = 0; virtual void on_send() = 0; -- cgit v1.2.3 From 11a1c0cc99af9629302184fac2b7adf3ac48516b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 28 Jul 2016 10:34:01 +0200 Subject: Always remove all the DNS sockets on an c-ares event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because c-ares may close one of its socket, even if it’s not the one that has the event. Otherwise we may not know when a socket has been removed from our poller (automatically, when close()ed) and this leads to bugs. --- louloulibs/network/dns_handler.cpp | 11 ++++++++++- louloulibs/network/dns_handler.hpp | 1 + louloulibs/network/dns_socket_handler.cpp | 13 ++++++++++--- louloulibs/network/dns_socket_handler.hpp | 6 +++++- 4 files changed, 26 insertions(+), 5 deletions(-) (limited to 'louloulibs') diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp index 5e19f8c..e267944 100644 --- a/louloulibs/network/dns_handler.cpp +++ b/louloulibs/network/dns_handler.cpp @@ -37,6 +37,7 @@ ares_channel& DNSHandler::get_channel() void DNSHandler::destroy() { + this->remove_all_sockets_from_poller(); this->socket_handlers.clear(); ::ares_destroy(this->channel); ::ares_library_cleanup(); @@ -95,7 +96,7 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) if (it == this->socket_handlers.end()) { this->socket_handlers.emplace(this->socket_handlers.begin(), - std::make_unique(poller, i)); + std::make_unique(poller, *this, i)); it = this->socket_handlers.begin(); } poller->add_socket_handler(it->get()); @@ -122,4 +123,12 @@ void DNSHandler::watch_dns_sockets(std::shared_ptr& poller) } } +void DNSHandler::remove_all_sockets_from_poller() +{ + for (const auto& socket_handler: this->socket_handlers) + { + socket_handler->remove_from_poller(); + } +} + #endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp index e0feb11..fd1729d 100644 --- a/louloulibs/network/dns_handler.hpp +++ b/louloulibs/network/dns_handler.hpp @@ -40,6 +40,7 @@ public: * and library. */ void destroy(); + void remove_all_sockets_from_poller(); ares_channel& get_channel(); static DNSHandler instance; diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp index faaabdb..5fd08cb 100644 --- a/louloulibs/network/dns_socket_handler.cpp +++ b/louloulibs/network/dns_socket_handler.cpp @@ -8,8 +8,10 @@ #include DNSSocketHandler::DNSSocketHandler(std::shared_ptr poller, + DNSHandler& handler, const socket_t socket): - SocketHandler(poller, socket) + SocketHandler(poller, socket), + handler(handler) { } @@ -21,7 +23,7 @@ void DNSSocketHandler::on_recv() { // always stop watching send and read events. We will re-watch them if the // next call to ares_fds tell us to - this->poller->remove_socket_handler(this->socket); + this->handler.remove_all_sockets_from_poller(); ::ares_process_fd(DNSHandler::instance.get_channel(), this->socket, ARES_SOCKET_BAD); } @@ -29,7 +31,7 @@ void DNSSocketHandler::on_send() { // always stop watching send and read events. We will re-watch them if the // next call to ares_fds tell us to - this->poller->remove_socket_handler(this->socket); + this->handler.remove_all_sockets_from_poller(); ::ares_process_fd(DNSHandler::instance.get_channel(), ARES_SOCKET_BAD, this->socket); } @@ -38,4 +40,9 @@ bool DNSSocketHandler::is_connected() const return true; } +void DNSSocketHandler::remove_from_poller() +{ + this->poller->remove_socket_handler(this->socket); +} + #endif /* CARES_FOUND */ diff --git a/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp index dba2f26..0570196 100644 --- a/louloulibs/network/dns_socket_handler.hpp +++ b/louloulibs/network/dns_socket_handler.hpp @@ -13,10 +13,12 @@ * (Poller reported it to be writable or readeable) */ +class DNSHandler; + class DNSSocketHandler: public SocketHandler { public: - explicit DNSSocketHandler(std::shared_ptr poller, const socket_t socket); + explicit DNSSocketHandler(std::shared_ptr poller, DNSHandler& handler, const socket_t socket); ~DNSSocketHandler() = default; DNSSocketHandler(const DNSSocketHandler&) = delete; DNSSocketHandler(DNSSocketHandler&&) = delete; @@ -38,8 +40,10 @@ public: * Always true, see the comment for connect() */ bool is_connected() const override final; + void remove_from_poller(); private: + DNSHandler& handler; }; #endif // CARES_FOUND -- cgit v1.2.3 From 5c78692fcd447d94be1eb43338f79790f53051f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 28 Jul 2016 10:14:04 +0200 Subject: Do not add 1ms to the timeout of our poller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Can’t remember why I did this, but that must be a stupid reason. Everything must work even with a timeout of 0. --- louloulibs/utils/timed_events_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/utils/timed_events_manager.cpp b/louloulibs/utils/timed_events_manager.cpp index b90a237..67d61fe 100644 --- a/louloulibs/utils/timed_events_manager.cpp +++ b/louloulibs/utils/timed_events_manager.cpp @@ -23,7 +23,7 @@ std::chrono::milliseconds TimedEventsManager::get_timeout() const { if (this->events.empty()) return utils::no_timeout; - return this->events.front().get_timeout() + std::chrono::milliseconds(1); + return this->events.front().get_timeout(); } std::size_t TimedEventsManager::execute_expired_events() -- cgit v1.2.3 From 0d1e0629e7efe07bacce6a22e45ddfd7652eb505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 28 Jul 2016 10:47:44 +0200 Subject: Fix the timeout test, now that we don't wait 1ms too much everytime --- louloulibs/utils/timed_events.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'louloulibs') diff --git a/louloulibs/utils/timed_events.cpp b/louloulibs/utils/timed_events.cpp index 6b936c4..68d009c 100644 --- a/louloulibs/utils/timed_events.cpp +++ b/louloulibs/utils/timed_events.cpp @@ -27,7 +27,7 @@ bool TimedEvent::is_after(const TimedEvent& other) const bool TimedEvent::is_after(const std::chrono::steady_clock::time_point& time_point) const { - return this->time_point >= time_point; + return this->time_point > time_point; } std::chrono::milliseconds TimedEvent::get_timeout() const -- cgit v1.2.3