summaryrefslogtreecommitdiff
path: root/louloulibs
diff options
context:
space:
mode:
Diffstat (limited to 'louloulibs')
-rw-r--r--louloulibs/CMakeLists.txt21
-rw-r--r--louloulibs/cmake/Modules/FindLIBUUID.cmake2
-rw-r--r--louloulibs/louloulibs.h.cmake4
-rw-r--r--louloulibs/network/credentials_manager.cpp47
-rw-r--r--louloulibs/network/credentials_manager.hpp1
-rw-r--r--louloulibs/network/dns_handler.cpp6
-rw-r--r--louloulibs/network/dns_socket_handler.cpp3
-rw-r--r--louloulibs/network/poller.cpp12
-rw-r--r--louloulibs/network/poller.hpp4
-rw-r--r--louloulibs/network/resolver.cpp26
-rw-r--r--louloulibs/network/resolver.hpp3
-rw-r--r--louloulibs/network/socket_handler.hpp2
-rw-r--r--louloulibs/network/tcp_socket_handler.cpp46
-rw-r--r--louloulibs/network/tcp_socket_handler.hpp9
-rw-r--r--louloulibs/utils/encoding.cpp21
-rw-r--r--louloulibs/utils/get_first_non_empty.cpp11
-rw-r--r--louloulibs/utils/get_first_non_empty.hpp20
-rw-r--r--louloulibs/utils/scopeguard.hpp7
-rw-r--r--louloulibs/utils/sha1.cpp33
-rw-r--r--louloulibs/utils/sha1.hpp2
-rw-r--r--louloulibs/utils/time.cpp70
-rw-r--r--louloulibs/utils/time.hpp10
-rw-r--r--louloulibs/xmpp/adhoc_commands_handler.cpp7
-rw-r--r--louloulibs/xmpp/adhoc_commands_handler.hpp4
-rw-r--r--louloulibs/xmpp/auth.cpp21
-rw-r--r--louloulibs/xmpp/auth.hpp6
-rw-r--r--louloulibs/xmpp/jid.hpp7
-rw-r--r--louloulibs/xmpp/roster.cpp21
-rw-r--r--louloulibs/xmpp/roster.hpp71
-rw-r--r--louloulibs/xmpp/xmpp_component.cpp99
-rw-r--r--louloulibs/xmpp/xmpp_component.hpp34
31 files changed, 357 insertions, 273 deletions
diff --git a/louloulibs/CMakeLists.txt b/louloulibs/CMakeLists.txt
index bf53504..908c35f 100644
--- a/louloulibs/CMakeLists.txt
+++ b/louloulibs/CMakeLists.txt
@@ -143,4 +143,25 @@ if(SYSTEMD_FOUND)
target_link_libraries(xmpplib ${SYSTEMD_LIBRARIES})
endif()
+#
+## Check if we have std::get_time
+#
+include(CheckCXXSourceCompiles)
+
+check_cxx_source_compiles("
+ #include <iomanip>
+ int main()
+ { std::get_time(nullptr, \"\"); }"
+ HAS_GET_TIME)
+
+mark_as_advanced(HAS_GET_TIME)
+
+check_cxx_source_compiles("
+ #include <iomanip>
+ int main()
+ { std::put_time(nullptr, \"\"); }"
+ HAS_PUT_TIME)
+
+mark_as_advanced(HAS_PUT_TIME)
+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/louloulibs.h.cmake ${CMAKE_BINARY_DIR}/src/louloulibs.h)
diff --git a/louloulibs/cmake/Modules/FindLIBUUID.cmake b/louloulibs/cmake/Modules/FindLIBUUID.cmake
index 17d3c42..f344249 100644
--- a/louloulibs/cmake/Modules/FindLIBUUID.cmake
+++ b/louloulibs/cmake/Modules/FindLIBUUID.cmake
@@ -19,7 +19,7 @@ include(FindPkgConfig)
pkg_check_modules(LIBUUID uuid)
if(NOT LIBUUID_FOUND)
- find_path(LIBUUID_INCLUDE_DIRS NAMES uuid.h
+ find_path(LIBUUID_INCLUDE_DIRS NAMES uuid/uuid.h
PATH_SUFFIXES uuid
DOC "The libuuid include directory")
diff --git a/louloulibs/louloulibs.h.cmake b/louloulibs/louloulibs.h.cmake
index 2feaf4e..6131b70 100644
--- a/louloulibs/louloulibs.h.cmake
+++ b/louloulibs/louloulibs.h.cmake
@@ -6,4 +6,6 @@
#cmakedefine BOTAN_FOUND
#cmakedefine CARES_FOUND
#cmakedefine SOFTWARE_VERSION "${SOFTWARE_VERSION}"
-#cmakedefine PROJECT_NAME "${PROJECT_NAME}" \ No newline at end of file
+#cmakedefine PROJECT_NAME "${PROJECT_NAME}"
+#cmakedefine HAS_GET_TIME
+#cmakedefine HAS_PUT_TIME
diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp
index ee83c3b..ed04d24 100644
--- a/louloulibs/network/credentials_manager.cpp
+++ b/louloulibs/network/credentials_manager.cpp
@@ -29,7 +29,7 @@ BasicCredentialsManager::BasicCredentialsManager(const TCPSocketHandler* const s
socket_handler(socket_handler),
trusted_fingerprint{}
{
- this->load_certs();
+ BasicCredentialsManager::load_certs();
}
void BasicCredentialsManager::set_trusted_fingerprint(const std::string& fingerprint)
@@ -62,17 +62,8 @@ void BasicCredentialsManager::verify_certificate_chain(const std::string& type,
}
}
-void BasicCredentialsManager::load_certs()
+bool BasicCredentialsManager::try_to_open_one_ca_bundle(const std::vector<std::string>& paths)
{
- // Only load the certificates the first time
- if (BasicCredentialsManager::certs_loaded)
- return;
- const std::string conf_path = Config::get("ca_file", "");
- std::vector<std::string> paths;
- if (conf_path.empty())
- paths = default_cert_files;
- else
- paths.push_back(conf_path);
for (const auto& path: paths)
{
try
@@ -87,25 +78,39 @@ void BasicCredentialsManager::load_certs()
// 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)
- {
+ Botan::X509_Certificate cert(bundle);
+ BasicCredentialsManager::certificate_store.add_certificate(std::move(cert));
+ } catch (const Botan::Decoding_Error& error) {
continue;
}
}
// Only use the first file that can successfully be read.
- goto success;
+ return true;
}
- catch (Botan::Stream_IO_Error& e)
+ catch (const Botan::Stream_IO_Error& e)
{
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:
- BasicCredentialsManager::certs_loaded = true;
+ return false;
+}
+
+void BasicCredentialsManager::load_certs()
+{
+ // Only load the certificates the first time
+ if (BasicCredentialsManager::certs_loaded)
+ return;
+ const std::string conf_path = Config::get("ca_file", "");
+ std::vector<std::string> paths;
+ if (conf_path.empty())
+ paths = default_cert_files;
+ else
+ paths.push_back(conf_path);
+
+ if (BasicCredentialsManager::try_to_open_one_ca_bundle(paths))
+ BasicCredentialsManager::certs_loaded = true;
+ else
+ log_warning("The CA could not be loaded, TLS negociation will probably fail.");
}
std::vector<Botan::Certificate_Store*> BasicCredentialsManager::trusted_certificate_authorities(const std::string&, const std::string&)
diff --git a/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp
index 0fc4b89..7557372 100644
--- a/louloulibs/network/credentials_manager.hpp
+++ b/louloulibs/network/credentials_manager.hpp
@@ -29,6 +29,7 @@ public:
private:
const TCPSocketHandler* const socket_handler;
+ static bool try_to_open_one_ca_bundle(const std::vector<std::string>& paths);
static void load_certs();
static Botan::Certificate_Store_In_Memory certificate_store;
static bool certs_loaded;
diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp
index e267944..fef0cfc 100644
--- a/louloulibs/network/dns_handler.cpp
+++ b/louloulibs/network/dns_handler.cpp
@@ -46,11 +46,7 @@ void DNSHandler::destroy()
void DNSHandler::gethostbyname(const std::string& name, ares_host_callback callback,
void* data, int family)
{
- if (family == AF_INET)
- ::ares_gethostbyname(this->channel, name.data(), family,
- callback, data);
- else
- ::ares_gethostbyname(this->channel, name.data(), family,
+ ::ares_gethostbyname(this->channel, name.data(), family,
callback, data);
}
diff --git a/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp
index 5fd08cb..403a5be 100644
--- a/louloulibs/network/dns_socket_handler.cpp
+++ b/louloulibs/network/dns_socket_handler.cpp
@@ -42,7 +42,8 @@ bool DNSSocketHandler::is_connected() const
void DNSSocketHandler::remove_from_poller()
{
- this->poller->remove_socket_handler(this->socket);
+ if (this->poller->is_managing_socket(this->socket))
+ this->poller->remove_socket_handler(this->socket);
}
#endif /* CARES_FOUND */
diff --git a/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp
index 8a6fd97..9f5bcfb 100644
--- a/louloulibs/network/poller.cpp
+++ b/louloulibs/network/poller.cpp
@@ -95,7 +95,7 @@ void Poller::remove_socket_handler(const socket_t socket)
void Poller::watch_send_events(SocketHandler* socket_handler)
{
#if POLLER == POLL
- for (size_t i = 0; i <= this->nfds; ++i)
+ for (size_t i = 0; i < this->nfds; ++i)
{
if (this->fds[i].fd == socket_handler->get_socket())
{
@@ -171,7 +171,7 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
// We cannot possibly have more ready events than the number of fds we are
// watching
assert(static_cast<unsigned int>(nb_events) <= this->nfds);
- for (size_t i = 0; i <= this->nfds && nb_events != 0; ++i)
+ 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)
@@ -186,7 +186,8 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
socket_handler->on_send();
nb_events--;
}
- else if (this->fds[i].revents & POLLOUT)
+ else if (this->fds[i].revents & POLLOUT ||
+ this->fds[i].revents & POLLIN)
{
socket_handler->connect();
nb_events--;
@@ -226,3 +227,8 @@ size_t Poller::size() const
{
return this->socket_handlers.size();
}
+
+bool Poller::is_managing_socket(const socket_t socket) const
+{
+ return (this->socket_handlers.find(socket) != this->socket_handlers.end());
+}
diff --git a/louloulibs/network/poller.hpp b/louloulibs/network/poller.hpp
index fc1a1a1..e39e438 100644
--- a/louloulibs/network/poller.hpp
+++ b/louloulibs/network/poller.hpp
@@ -74,6 +74,10 @@ public:
* Returns the number of SocketHandlers managed by the poller.
*/
size_t size() const;
+ /**
+ * Whether the given socket is managed by the poller
+ */
+ bool is_managing_socket(const socket_t socket) const;
private:
/**
diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp
index 9d6de23..2987aaa 100644
--- a/louloulibs/network/resolver.cpp
+++ b/louloulibs/network/resolver.cpp
@@ -2,6 +2,7 @@
#include <network/resolver.hpp>
#include <string.h>
#include <arpa/inet.h>
+#include <cstdlib>
using namespace std::string_literals;
@@ -116,12 +117,13 @@ void Resolver::fill_ares_addrinfo4(const struct hostent* hostent)
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;
+ struct sockaddr_in* ai_addr = new struct sockaddr_in;
+
+ ai_addr->sin_family = hostent->h_addrtype;
+ ai_addr->sin_port = htons(std::strtoul(this->port.data(), nullptr, 10));
+ ai_addr->sin_addr.s_addr = (*address)->s_addr;
- current->ai_addr = reinterpret_cast<struct sockaddr*>(addr);
+ current->ai_addr = reinterpret_cast<struct sockaddr*>(ai_addr);
current->ai_next = nullptr;
current->ai_canonname = nullptr;
@@ -147,14 +149,14 @@ void Resolver::fill_ares_addrinfo6(const struct hostent* hostent)
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;
+ struct sockaddr_in6* ai_addr = new struct sockaddr_in6;
+ ai_addr->sin6_family = hostent->h_addrtype;
+ ai_addr->sin6_port = htons(std::strtoul(this->port.data(), nullptr, 10));
+ ::memcpy(ai_addr->sin6_addr.s6_addr, (*address)->s6_addr, sizeof(ai_addr->sin6_addr.s6_addr));
+ ai_addr->sin6_flowinfo = 0;
+ ai_addr->sin6_scope_id = 0;
- current->ai_addr = reinterpret_cast<struct sockaddr*>(addr);
+ current->ai_addr = reinterpret_cast<struct sockaddr*>(ai_addr);
current->ai_canonname = nullptr;
current->ai_next = prev;
diff --git a/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp
index afe6e2b..29e6f3a 100644
--- a/louloulibs/network/resolver.hpp
+++ b/louloulibs/network/resolver.hpp
@@ -11,8 +11,9 @@
#include <sys/socket.h>
#include <netdb.h>
-struct AddrinfoDeleter
+class AddrinfoDeleter
{
+ public:
void operator()(struct addrinfo* addr)
{
#ifdef CARES_FOUND
diff --git a/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp
index eeb41fe..ea79a18 100644
--- a/louloulibs/network/socket_handler.hpp
+++ b/louloulibs/network/socket_handler.hpp
@@ -14,7 +14,7 @@ public:
poller(poller),
socket(socket)
{}
- virtual ~SocketHandler() {}
+ virtual ~SocketHandler() = default;
SocketHandler(const SocketHandler&) = delete;
SocketHandler(SocketHandler&&) = delete;
SocketHandler& operator=(const SocketHandler&) = delete;
diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp
index 5420b1c..1dddde5 100644
--- a/louloulibs/network/tcp_socket_handler.cpp
+++ b/louloulibs/network/tcp_socket_handler.cpp
@@ -80,7 +80,7 @@ void TCPSocketHandler::init_socket(const struct addrinfo* rp)
}
if (!rp)
log_error("Failed to bind socket to ", this->bind_addr, ": ",
- strerror(bind_error));
+ strerror(errno));
else
log_info("Socket successfully bound to ", this->bind_addr);
}
@@ -103,8 +103,6 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po
this->port = port;
this->use_tls = tls;
- utils::ScopeGuard sg;
-
struct addrinfo* addr_res;
if (!this->connecting)
@@ -181,6 +179,8 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po
if (this->use_tls)
this->start_tls();
#endif
+ this->connection_date = std::chrono::system_clock::now();
+
this->on_connected();
return ;
}
@@ -294,7 +294,8 @@ void TCPSocketHandler::on_send()
// unconsting the content of s is ok, sendmsg will never modify it
msg_iov[msg.msg_iovlen].iov_base = const_cast<char*>(s.data());
msg_iov[msg.msg_iovlen].iov_len = s.size();
- if (++msg.msg_iovlen == UIO_FASTIOV)
+ msg.msg_iovlen++;
+ if (msg.msg_iovlen == UIO_FASTIOV)
break;
}
ssize_t res = ::sendmsg(this->socket, &msg, MSG_NOSIGNAL);
@@ -307,23 +308,24 @@ void TCPSocketHandler::on_send()
else
{
// remove all the strings that were successfully sent.
- for (auto it = this->out_buf.begin();
- it != this->out_buf.end();)
+ auto it = this->out_buf.begin();
+ while (it != this->out_buf.end())
{
- if (static_cast<size_t>(res) >= (*it).size())
+ if (static_cast<size_t>(res) >= it->size())
{
- res -= (*it).size();
- it = this->out_buf.erase(it);
+ res -= it->size();
+ ++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);
+ *it = it->substr(res, std::string::npos);
break;
}
}
+ this->out_buf.erase(this->out_buf.begin(), it);
if (this->out_buf.empty())
this->poller->stop_watching_send_events(this);
}
@@ -397,6 +399,16 @@ bool TCPSocketHandler::is_connecting() const
return this->connecting || this->resolver.is_resolving();
}
+bool TCPSocketHandler::is_using_tls() const
+{
+ return this->use_tls;
+}
+
+std::string TCPSocketHandler::get_port() const
+{
+ return this->port;
+}
+
void* TCPSocketHandler::get_receive_buffer(const size_t) const
{
return nullptr;
@@ -418,15 +430,14 @@ void TCPSocketHandler::start_tls()
void TCPSocketHandler::tls_recv()
{
static constexpr size_t buf_size = 4096;
- char recv_buf[buf_size];
+ Botan::byte 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();
try {
- this->tls->received_data(reinterpret_cast<const Botan::byte*>(recv_buf),
- static_cast<size_t>(size));
+ this->tls->received_data(recv_buf, static_cast<size_t>(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
@@ -449,9 +460,8 @@ void TCPSocketHandler::tls_send(std::string&& data)
const bool was_active = this->tls->is_active();
if (!this->pre_buf.empty())
{
- this->tls->send(reinterpret_cast<const Botan::byte*>(this->pre_buf.data()),
- this->pre_buf.size());
- this->pre_buf = "";
+ this->tls->send(this->pre_buf.data(), this->pre_buf.size());
+ this->pre_buf.clear();
}
if (!data.empty())
this->tls->send(reinterpret_cast<const Botan::byte*>(data.data()),
@@ -460,7 +470,9 @@ void TCPSocketHandler::tls_send(std::string&& data)
this->on_tls_activated();
}
else
- this->pre_buf += data;
+ this->pre_buf.insert(this->pre_buf.end(),
+ std::make_move_iterator(data.begin()),
+ std::make_move_iterator(data.end()));
}
void TCPSocketHandler::tls_data_cb(const Botan::byte* data, size_t size)
diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp
index b0ba493..20a3e5a 100644
--- a/louloulibs/network/tcp_socket_handler.hpp
+++ b/louloulibs/network/tcp_socket_handler.hpp
@@ -13,6 +13,7 @@
#include <netinet/in.h>
#include <netdb.h>
+#include <chrono>
#include <vector>
#include <memory>
#include <string>
@@ -106,6 +107,9 @@ public:
#endif
bool is_connected() const override final;
bool is_connecting() const;
+ bool is_using_tls() const;
+ std::string get_port() const;
+ std::chrono::system_clock::time_point connection_date;
private:
/**
@@ -266,9 +270,6 @@ private:
* An additional buffer to keep data that the user wants to send, but
* cannot because the handshake is not done.
*/
- std::string pre_buf;
+ std::vector<Botan::byte> pre_buf;
#endif // BOTAN_FOUND
};
-
-
-
diff --git a/louloulibs/utils/encoding.cpp b/louloulibs/utils/encoding.cpp
index 507f38a..60f2212 100644
--- a/louloulibs/utils/encoding.cpp
+++ b/louloulibs/utils/encoding.cpp
@@ -75,13 +75,12 @@ namespace utils
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;});
+ std::vector<char> res(original.size(), '\0');
// pointer where we write valid chars
- unsigned char* r = res;
+ char* r = res.data();
- const unsigned char* str = reinterpret_cast<const unsigned char*>(original.c_str());
+ const char* str = original.c_str();
std::bitset<20> codepoint;
while (*str)
@@ -140,7 +139,7 @@ namespace utils
else
throw std::runtime_error("Invalid UTF-8 passed to remove_invalid_xml_chars");
}
- return std::string(reinterpret_cast<char*>(res), r-res);
+ return {res.data(), static_cast<size_t>(r - res.data())};
}
std::string convert_to_utf8(const std::string& str, const char* charset)
@@ -152,7 +151,7 @@ namespace utils
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); });
+ const auto sg = utils::make_scope_guard([&cd](auto&&){ iconv_close(cd); });
size_t inbytesleft = str.size();
@@ -169,7 +168,7 @@ namespace utils
char* outbuf_ptr = outbuf;
// Make sure outbuf is always deleted when we leave this function
- sg.add_callback([&]{ delete[] outbuf; });
+ const auto sg2 = utils::make_scope_guard([outbuf](auto&&){ delete[] outbuf; });
bool done = false;
while (done == false)
@@ -197,12 +196,8 @@ namespace utils
outbuf_ptr++;
done = true;
break;
- case E2BIG:
- // This should never happen
- done = true;
- break;
- default:
- // This should happen even neverer
+ case E2BIG: // This should never happen
+ default: // This should happen even neverer
done = true;
break;
}
diff --git a/louloulibs/utils/get_first_non_empty.cpp b/louloulibs/utils/get_first_non_empty.cpp
new file mode 100644
index 0000000..5b3bedb
--- /dev/null
+++ b/louloulibs/utils/get_first_non_empty.cpp
@@ -0,0 +1,11 @@
+#include <utils/get_first_non_empty.hpp>
+
+bool is_empty(const std::string& val)
+{
+ return val.empty();
+}
+
+bool is_empty(const int& val)
+{
+ return val == 0;
+}
diff --git a/louloulibs/utils/get_first_non_empty.hpp b/louloulibs/utils/get_first_non_empty.hpp
new file mode 100644
index 0000000..a38f5fb
--- /dev/null
+++ b/louloulibs/utils/get_first_non_empty.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <string>
+
+bool is_empty(const std::string& val);
+bool is_empty(const int& val);
+
+template <typename T>
+T get_first_non_empty(T&& last)
+{
+ return last;
+}
+
+template <typename T, typename... Args>
+T get_first_non_empty(T&& first, Args&&... args)
+{
+ if (!is_empty(first))
+ return first;
+ return get_first_non_empty(std::forward<Args>(args)...);
+}
diff --git a/louloulibs/utils/scopeguard.hpp b/louloulibs/utils/scopeguard.hpp
index ee1e2ef..cd0e89e 100644
--- a/louloulibs/utils/scopeguard.hpp
+++ b/louloulibs/utils/scopeguard.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <functional>
+#include <memory>
#include <vector>
/**
@@ -85,5 +86,11 @@ private:
};
+template<typename F>
+auto make_scope_guard(F&& f)
+{
+ return std::unique_ptr<void, std::decay_t<F>>{(void*)1, std::forward<F>(f)};
+}
+
}
diff --git a/louloulibs/utils/sha1.cpp b/louloulibs/utils/sha1.cpp
index 76476df..f75bc2a 100644
--- a/louloulibs/utils/sha1.cpp
+++ b/louloulibs/utils/sha1.cpp
@@ -119,36 +119,3 @@ uint8_t* sha1_result(sha1nfo *s) {
// 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; i<BLOCK_LENGTH; i++) {
- sha1_writebyte(s, s->keyBuffer[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; i<BLOCK_LENGTH; i++) sha1_writebyte(s, s->keyBuffer[i] ^ HMAC_OPAD);
- for (i=0; i<HASH_LENGTH; i++) sha1_writebyte(s, s->innerHash[i]);
- return sha1_result(s);
-}
diff --git a/louloulibs/utils/sha1.hpp b/louloulibs/utils/sha1.hpp
index d02de75..d436782 100644
--- a/louloulibs/utils/sha1.hpp
+++ b/louloulibs/utils/sha1.hpp
@@ -31,5 +31,3 @@ 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/time.cpp b/louloulibs/utils/time.cpp
new file mode 100644
index 0000000..afd6117
--- /dev/null
+++ b/louloulibs/utils/time.cpp
@@ -0,0 +1,70 @@
+#include <utils/time.hpp>
+#include <time.h>
+
+#include <sstream>
+#include <iomanip>
+#include <locale>
+
+#include "louloulibs.h"
+
+namespace utils
+{
+std::string to_string(const std::time_t& timestamp)
+{
+ constexpr std::size_t stamp_size = 21;
+ char date_buf[stamp_size];
+ if (std::strftime(date_buf, stamp_size, "%FT%TZ", std::gmtime(&timestamp)) != stamp_size - 1)
+ return "";
+ return {std::begin(date_buf), std::end(date_buf) - 1};
+}
+
+std::time_t parse_datetime(const std::string& stamp)
+{
+ static const char* format = "%Y-%m-%dT%H:%M:%S";
+ std::tm t = {};
+#ifdef HAS_GET_TIME
+ std::istringstream ss(stamp);
+ ss.imbue(std::locale("en_US.utf-8"));
+
+ std::string timezone;
+ ss >> std::get_time(&t, format) >> timezone;
+ if (ss.fail())
+ return -1;
+#else
+ /* Y - m - d T H : M : S */
+ constexpr std::size_t stamp_size_without_tz = 4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2;
+ if (!strptime(stamp.data(), format, &t)) {
+ return -1;
+ }
+ const std::string timezone(stamp.data() + stamp_size_without_tz);
+#endif
+
+ if (timezone.empty())
+ return -1;
+
+ if (timezone.compare(0, 1, "Z") != 0)
+ {
+ std::stringstream tz_ss;
+ tz_ss << timezone;
+ int multiplier = -1;
+ char prefix;
+ int hours;
+ char sep;
+ int minutes;
+ tz_ss >> prefix >> hours >> sep >> minutes;
+ if (tz_ss.fail())
+ return -1;
+ if (prefix == '-')
+ multiplier = +1;
+ else if (prefix != '+')
+ return -1;
+
+ t.tm_hour += multiplier * hours;
+ t.tm_min += multiplier * minutes;
+ }
+ return ::timegm(&t);
+}
+
+}
+
+
diff --git a/louloulibs/utils/time.hpp b/louloulibs/utils/time.hpp
new file mode 100644
index 0000000..c71cd9c
--- /dev/null
+++ b/louloulibs/utils/time.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <ctime>
+#include <string>
+
+namespace utils
+{
+std::string to_string(const std::time_t& timestamp);
+std::time_t parse_datetime(const std::string& stamp);
+} \ No newline at end of file
diff --git a/louloulibs/xmpp/adhoc_commands_handler.cpp b/louloulibs/xmpp/adhoc_commands_handler.cpp
index 17c4e67..540cac0 100644
--- a/louloulibs/xmpp/adhoc_commands_handler.cpp
+++ b/louloulibs/xmpp/adhoc_commands_handler.cpp
@@ -15,9 +15,12 @@ const std::map<const std::string, const AdhocCommand>& AdhocCommandsHandler::get
return this->commands;
}
-std::map<const std::string, const AdhocCommand>& AdhocCommandsHandler::get_commands()
+void AdhocCommandsHandler::add_command(std::string name, AdhocCommand command)
{
- return this->commands;
+ const auto found = this->commands.find(name);
+ if (found != this->commands.end())
+ throw std::runtime_error("Trying to add an ad-hoc command that already exist: "s + name);
+ this->commands.emplace(std::make_pair(std::move(name), std::move(command)));
}
XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, const std::string& to, XmlNode command_node)
diff --git a/louloulibs/xmpp/adhoc_commands_handler.hpp b/louloulibs/xmpp/adhoc_commands_handler.hpp
index 91eb5bd..e37d913 100644
--- a/louloulibs/xmpp/adhoc_commands_handler.hpp
+++ b/louloulibs/xmpp/adhoc_commands_handler.hpp
@@ -31,9 +31,9 @@ public:
*/
const std::map<const std::string, const AdhocCommand>& get_commands() const;
/**
- * This one can be used to add new commands.
+ * Add a command into the list, associated with the given name
*/
- std::map<const std::string, const AdhocCommand>& get_commands();
+ void add_command(std::string name, AdhocCommand command);
/**
* 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
diff --git a/louloulibs/xmpp/auth.cpp b/louloulibs/xmpp/auth.cpp
new file mode 100644
index 0000000..c20f95d
--- /dev/null
+++ b/louloulibs/xmpp/auth.cpp
@@ -0,0 +1,21 @@
+#include <xmpp/auth.hpp>
+
+#include <utils/sha1.hpp>
+
+#include <iomanip>
+#include <sstream>
+
+std::string get_handshake_digest(const std::string& stream_id, const std::string& secret)
+{
+ sha1nfo sha1;
+ sha1_init(&sha1);
+ sha1_write(&sha1, stream_id.data(), stream_id.size());
+ sha1_write(&sha1, secret.data(), secret.size());
+ const uint8_t* result = sha1_result(&sha1);
+
+ std::ostringstream digest;
+ for (int i = 0; i < HASH_LENGTH; i++)
+ digest << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(result[i]);
+
+ return digest.str();
+}
diff --git a/louloulibs/xmpp/auth.hpp b/louloulibs/xmpp/auth.hpp
new file mode 100644
index 0000000..34a2116
--- /dev/null
+++ b/louloulibs/xmpp/auth.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <string>
+
+std::string get_handshake_digest(const std::string& stream_id, const std::string& secret);
+
diff --git a/louloulibs/xmpp/jid.hpp b/louloulibs/xmpp/jid.hpp
index 08327ef..85e835c 100644
--- a/louloulibs/xmpp/jid.hpp
+++ b/louloulibs/xmpp/jid.hpp
@@ -26,7 +26,12 @@ public:
}
std::string full() const
{
- return this->local + "@" + this->domain + "/" + this->resource;
+ std::string res = this->domain;
+ if (!this->local.empty())
+ res = this->local + "@" + this->domain;
+ if (!this->resource.empty())
+ res += "/" + this->resource;
+ return res;
}
};
diff --git a/louloulibs/xmpp/roster.cpp b/louloulibs/xmpp/roster.cpp
deleted file mode 100644
index a14a384..0000000
--- a/louloulibs/xmpp/roster.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include <xmpp/roster.hpp>
-
-RosterItem::RosterItem(const std::string& jid, const std::string& name,
- std::vector<std::string>& 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
deleted file mode 100644
index aa1b449..0000000
--- a/louloulibs/xmpp/roster.hpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#pragma once
-
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-class RosterItem
-{
-public:
- RosterItem(const std::string& jid, const std::string& name,
- std::vector<std::string>& 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<std::string> groups;
-
-private:
-};
-
-/**
- * Keep track of the last known stat of a JID's roster
- */
-class Roster
-{
-public:
- Roster() = default;
- ~Roster() = default;
-
- void clear();
-
- template <typename... ArgsType>
- RosterItem* add_item(ArgsType&&... args)
- {
- this->items.emplace_back(std::forward<ArgsType>(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<RosterItem>& get_items() const
- {
- return this->items;
- }
-
-private:
- std::vector<RosterItem> items;
-
- Roster(const Roster&) = delete;
- Roster(Roster&&) = delete;
- Roster& operator=(const Roster&) = delete;
- Roster& operator=(Roster&&) = delete;
-};
-
-
diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp
index e87cdf7..fa8b0a5 100644
--- a/louloulibs/xmpp/xmpp_component.cpp
+++ b/louloulibs/xmpp/xmpp_component.cpp
@@ -5,16 +5,18 @@
#include <xmpp/xmpp_component.hpp>
#include <config/config.hpp>
+#include <utils/time.hpp>
+#include <xmpp/auth.hpp>
#include <xmpp/jid.hpp>
-#include <utils/sha1.hpp>
#include <stdexcept>
#include <iostream>
#include <set>
-#include <stdio.h>
+#include <uuid/uuid.h>
-#include <uuid.h>
+#include <cstdlib>
+#include <set>
#include <louloulibs.h>
#ifdef SYSTEMD_FOUND
@@ -136,17 +138,7 @@ void XmppComponent::on_remote_stream_open(const XmlNode& node)
}
// 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';
-
- auto data = "<handshake xmlns='" COMPONENT_NS "'>"s + digest + "</handshake>";
+ auto data = "<handshake xmlns='" COMPONENT_NS "'>"s + get_handshake_digest(this->stream_id, this->secret) + "</handshake>";
log_debug("XMPP SENDING: ", data);
this->send_data(std::move(data));
}
@@ -230,9 +222,8 @@ void XmppComponent::close_document()
this->doc_open = false;
}
-void XmppComponent::handle_handshake(const Stanza& stanza)
+void XmppComponent::handle_handshake(const Stanza&)
{
- (void)stanza;
this->authenticated = true;
this->ever_auth = true;
log_info("Authenticated with the XMPP server");
@@ -270,7 +261,8 @@ 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)
+void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, const std::string& to,
+ const std::string& type, const bool fulljid, const bool nocopy)
{
XmlNode node("message");
node["to"] = to;
@@ -291,6 +283,18 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con
html.add_child(std::move(std::get<1>(body)));
node.add_child(std::move(html));
}
+
+ if (nocopy)
+ {
+ XmlNode private_node("private");
+ private_node["xmlns"] = "urn:xmpp:carbons:2";
+ node.add_child(std::move(private_node));
+
+ XmlNode nocopy("no-copy");
+ nocopy["xmlns"] = "urn:xmpp:hints";
+ node.add_child(std::move(nocopy));
+ }
+
this->send_stanza(node);
}
@@ -363,31 +367,6 @@ void XmppComponent::send_invalid_room_error(const std::string& muc_name,
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;
- 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;
- 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: <nick>!<server>@" +
- this->served_hostname);
- error.add_child(std::move(text));
- message.add_child(std::move(error));
- this->send_stanza(message);
-}
-
void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, const std::string& to, const std::string& who)
{
XmlNode message("message");
@@ -426,6 +405,29 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str
this->send_stanza(message);
}
+void XmppComponent::send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body_txt, const std::string& jid_to, std::time_t timestamp)
+{
+ Stanza message("message");
+ message["to"] = jid_to;
+ if (!nick.empty())
+ message["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
+ else
+ message["from"] = muc_name + "@" + this->served_hostname;
+ message["type"] = "groupchat";
+
+ XmlNode body("body");
+ body.set_inner(body_txt);
+ message.add_child(std::move(body));
+
+ XmlNode delay("delay");
+ delay["xmlns"] = DELAY_NS;
+ delay["from"] = muc_name + "@" + this->served_hostname;
+ delay["stamp"] = utils::to_string(timestamp);
+
+ message.add_child(std::move(delay));
+ 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");
@@ -483,11 +485,8 @@ void XmppComponent::send_nick_change(const std::string& muc_name,
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)
+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, const bool self)
{
Stanza presence("presence");
presence["from"] = muc_name + "@" + this->served_hostname + "/" + target;
@@ -509,6 +508,12 @@ void XmppComponent::kick_user(const std::string& muc_name,
XmlNode status("status");
status["code"] = "307";
x.add_child(std::move(status));
+ if (self)
+ {
+ XmlNode status("status");
+ status["code"] = "110";
+ x.add_child(std::move(status));
+ }
presence.add_child(std::move(x));
this->send_stanza(presence);
}
diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp
index 5fc6d2e..5f5f937 100644
--- a/louloulibs/xmpp/xmpp_component.hpp
+++ b/louloulibs/xmpp/xmpp_component.hpp
@@ -9,6 +9,7 @@
#include <unordered_map>
#include <memory>
#include <string>
+#include <ctime>
#include <map>
#define STREAM_NS "http://etherx.jabber.org/streams"
@@ -25,6 +26,13 @@
#define VERSION_NS "jabber:iq:version"
#define ADHOC_NS "http://jabber.org/protocol/commands"
#define PING_NS "urn:xmpp:ping"
+#define DELAY_NS "urn:xmpp:delay"
+#define MAM_NS "urn:xmpp:mam:1"
+#define FORWARD_NS "urn:xmpp:forward:0"
+#define CLIENT_NS "jabber:client"
+#define DATAFORM_NS "jabber:x:data"
+#define RSM_NS "http://jabber.org/protocol/rsm"
+#define MUC_TRAFFIC_NS "http://jabber.org/protocol/muc#traffic"
/**
* An XMPP component, communicating with an XMPP server using the protocole
@@ -101,9 +109,8 @@ public:
* 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);
+ void send_message(const std::string& from, Xmpp::body&& body, const std::string& to,
+ const std::string& type, const bool fulljid, const bool nocopy=false);
/**
* Send a join from a new participant
*/
@@ -121,12 +128,6 @@ public:
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, const std::string& who);
@@ -135,6 +136,11 @@ public:
*/
void send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& body, const std::string& jid_to);
/**
+ * Send a message, with a <delay/> element, part of a MUC history
+ */
+ void send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body,
+ const std::string& jid_to, const std::time_t timestamp);
+ /**
* 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);
@@ -151,11 +157,8 @@ public:
/**
* 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);
+ 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, const bool self);
/**
* Send a generic presence error
*/
@@ -208,6 +211,9 @@ public:
virtual void after_handshake() {}
+ const std::string& get_served_hostname() const
+ { return this->served_hostname; }
+
/**
* Whether or not we ever succeeded our authentication to the XMPP server
*/