From cad70ba453a7877b64c0f23a4b4f34db9d1a0957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 2 Aug 2017 21:47:21 +0200 Subject: Re-add a removed (by mistake) pointer null check --- src/xmpp/biboumi_adhoc_commands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index 60af506..65f9966 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -409,7 +409,7 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com else if (field->get_tag("var") == "pass" && value) options.col() = value->get_inner(); - else if (field->get_tag("var") == "after_connect_command") + else if (field->get_tag("var") == "after_connect_command" && value) options.col() = value->get_inner(); else if (field->get_tag("var") == "username" && value) -- cgit v1.2.3 From 9648fd286c16dc1b9154a7940820a5c7043b4836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 19 Aug 2017 13:00:19 +0200 Subject: Display the correct error message when we fail to open the sqlite3 db fix #3290 --- src/database/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/database/database.cpp b/src/database/database.cpp index 85c675e..0068b24 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -25,7 +25,7 @@ void Database::open(const std::string& filename) auto res = sqlite3_open_v2(filename.data(), &new_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); if (res != SQLITE_OK) { - log_error("Failed to open database file ", filename, ": ", sqlite3_errmsg(Database::db)); + log_error("Failed to open database file ", filename, ": ", sqlite3_errmsg(new_db)); throw std::runtime_error(""); } Database::close(); -- cgit v1.2.3 From 2a62b8b0823435e14675ae9bbe281ddd37206fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 19 Aug 2017 13:00:56 +0200 Subject: Fix a sqlite3 leak when the database fails to open --- src/database/database.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/database/database.cpp b/src/database/database.cpp index 0068b24..266b17e 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -23,12 +23,13 @@ void Database::open(const std::string& filename) // not, just leave things untouched sqlite3* new_db; auto res = sqlite3_open_v2(filename.data(), &new_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); + Database::close(); if (res != SQLITE_OK) { log_error("Failed to open database file ", filename, ": ", sqlite3_errmsg(new_db)); + sqlite3_close_v2(new_db); throw std::runtime_error(""); } - Database::close(); Database::db = new_db; Database::muc_log_lines.create(Database::db); Database::muc_log_lines.upgrade(Database::db); -- cgit v1.2.3 From c23c99dac109b3cfd53c72dbcbaf8b483bd6f4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 24 Aug 2017 23:55:32 +0200 Subject: =?UTF-8?q?Don=E2=80=99t=20forget=20to=20remove=20the=20user=20fro?= =?UTF-8?q?m=20the=20channel,=20when=20kicked?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix #3291 --- src/irc/irc_client.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 46dbdbe..ba593c9 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -1073,12 +1073,18 @@ void IrcClient::on_nick(const IrcMessage& message) void IrcClient::on_kick(const IrcMessage& message) { const std::string chan_name = utils::tolower(message.arguments[0]); - const std::string target = message.arguments[1]; + const std::string target_nick = message.arguments[1]; const std::string reason = message.arguments[2]; IrcChannel* channel = this->get_channel(chan_name); if (!channel->joined) return ; - const bool self = channel->get_self()->nick == target; + const IrcUser* target = channel->find_user(target_nick); + if (!target) + { + log_warning("Received a KICK command from a nick absent from the channel."); + return; + } + const bool self = channel->get_self() == target; if (self) channel->joined = false; IrcUser author(message.prefix); @@ -1086,7 +1092,8 @@ void IrcClient::on_kick(const IrcMessage& message) iid.set_local(chan_name); iid.set_server(this->hostname); iid.type = Iid::Type::Channel; - this->bridge.kick_muc_user(std::move(iid), target, reason, author.nick, self); + this->bridge.kick_muc_user(std::move(iid), target_nick, reason, author.nick, self); + channel->remove_user(target); } void IrcClient::on_invite(const IrcMessage& message) -- cgit v1.2.3 From 1997fb5c6a5f791960575a31bd34dfd24cf96a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 24 Aug 2017 23:56:00 +0200 Subject: Small refactor in on_quit() --- src/irc/irc_client.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index ba593c9..7776c8d 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -1017,19 +1017,17 @@ void IrcClient::on_quit(const IrcMessage& message) const std::string& chan_name = pair.first; IrcChannel* channel = pair.second.get(); const IrcUser* user = channel->find_user(message.prefix); + if (!user) + continue; bool self = false; if (user == channel->get_self()) self = true; - if (user) - { - std::string nick = user->nick; - channel->remove_user(user); - Iid iid; - iid.set_local(chan_name); - iid.set_server(this->hostname); - iid.type = Iid::Type::Channel; - this->bridge.send_muc_leave(iid, std::move(nick), txt, self, false); - } + Iid iid; + iid.set_local(chan_name); + iid.set_server(this->hostname); + iid.type = Iid::Type::Channel; + this->bridge.send_muc_leave(iid, user->nick, txt, self, false); + channel->remove_user(user); } } -- cgit v1.2.3 From 655151d88a6ab948949b73682c3a76a0274eb10c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 26 Aug 2017 13:51:15 +0200 Subject: Cache the encoding_in database value, to avoid doing a query for each message --- src/bridge/bridge.cpp | 7 ++----- src/database/database.cpp | 1 + src/database/database.hpp | 35 +++++++++++++++++++++++++++++++++++ src/xmpp/biboumi_adhoc_commands.cpp | 4 ++-- 4 files changed, 40 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index e0cb36d..9fd0a5b 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -22,15 +22,12 @@ static std::string in_encoding_for(const Bridge& bridge, const Iid& iid) { #ifdef USE_DATABASE const auto jid = bridge.get_bare_jid(); - auto options = Database::get_irc_channel_options_with_server_default(jid, iid.get_server(), iid.get_local()); - auto result = options.col(); - if (!result.empty()) - return result; + return Database::get_encoding_in(jid, iid.get_server(), iid.get_local()); #else (void)bridge; (void)iid; -#endif return {"ISO-8859-1"}; +#endif } Bridge::Bridge(std::string user_jid, BiboumiComponent& xmpp, std::shared_ptr& poller): diff --git a/src/database/database.cpp b/src/database/database.cpp index 266b17e..0f2349d 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -14,6 +14,7 @@ Database::GlobalOptionsTable Database::global_options("GlobalOptions_"); Database::IrcServerOptionsTable Database::irc_server_options("IrcServerOptions_"); Database::IrcChannelOptionsTable Database::irc_channel_options("IrcChannelOptions_"); Database::RosterTable Database::roster("roster"); +std::map Database::encoding_in_cache{}; void Database::open(const std::string& filename) diff --git a/src/database/database.hpp b/src/database/database.hpp index c00c938..f4b2ecd 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -13,6 +13,7 @@ #include #include +#include class Database @@ -140,7 +141,41 @@ class Database static RosterTable roster; static sqlite3* db; + /** + * Some caches, to avoid doing very frequent query requests for a few options. + */ + using CacheKey = std::tuple; + + static EncodingIn::real_type get_encoding_in(const std::string& owner, + const std::string& server, + const std::string& channel) + { + CacheKey channel_key{owner, server, channel}; + auto it = Database::encoding_in_cache.find(channel_key); + if (it == Database::encoding_in_cache.end()) + { + auto options = Database::get_irc_channel_options_with_server_default(owner, server, channel); + EncodingIn::real_type result = options.col(); + if (result.empty()) + result = "ISO-8859-1"; + it = Database::encoding_in_cache.insert(std::make_pair(channel_key, result)).first; + } + return it->second; + } + static void invalidate_encoding_in_cache(const std::string& owner, + const std::string& server, + const std::string& channel) + { + CacheKey channel_key{owner, server, channel}; + Database::encoding_in_cache.erase(channel_key); + } + static void invalidate_encoding_in_cache() + { + Database::encoding_in_cache.clear(); + } + private: static std::string gen_uuid(); + static std::map encoding_in_cache; }; #endif /* USE_DATABASE */ diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index 65f9966..d78dc98 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -430,7 +430,7 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com options.col() = value->get_inner(); } - + Database::invalidate_encoding_in_cache(); options.save(Database::db); command_node.delete_all_children(); @@ -599,7 +599,7 @@ bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const } } - + Database::invalidate_encoding_in_cache(requester.bare(), iid.get_server(), iid.get_local()); options.save(Database::db); } return true; -- cgit v1.2.3 From 2c717d347d796a2b007331c42d78146e156eaea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 26 Aug 2017 17:40:54 +0200 Subject: Add an index for the muc_log_line table This immensely speeds up the archive select queries. fix #3292 --- src/database/database.cpp | 3 +++ src/database/index.hpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/database/index.hpp (limited to 'src') diff --git a/src/database/database.cpp b/src/database/database.cpp index 0f2349d..f706528 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include sqlite3* Database::db; @@ -42,6 +44,7 @@ void Database::open(const std::string& filename) Database::irc_channel_options.upgrade(Database::db); Database::roster.create(Database::db); Database::roster.upgrade(Database::db); + create_index(Database::db, "archive_index", Database::muc_log_lines.get_name()); } diff --git a/src/database/index.hpp b/src/database/index.hpp new file mode 100644 index 0000000..5924779 --- /dev/null +++ b/src/database/index.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include +#include + +namespace +{ +template +typename std::enable_if::type +add_column_name(std::string&) +{ } + +template +typename std::enable_if::type +add_column_name(std::string& out) +{ + using ColumnType = typename std::remove_reference(std::declval>()))>::type; + out += ColumnType::name; + if (N != sizeof...(T) - 1) + out += ","; + add_column_name(out); +} +} + +template +void create_index(sqlite3* db, const std::string& name, const std::string& table) +{ + std::string res{"CREATE INDEX IF NOT EXISTS "}; + res += name + " ON " + table + "("; + add_column_name<0, Columns...>(res); + res += ")"; + + char* error; + const auto result = sqlite3_exec(db, res.data(), nullptr, nullptr, &error); + if (result != SQLITE_OK) + { + log_error("Error executing query: ", error); + sqlite3_free(error); + } +} -- cgit v1.2.3 From 25243f53c2479e2fda0f1a05d1589c8214b70b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 27 Aug 2017 14:32:29 +0200 Subject: =?UTF-8?q?In=20fixed=20mode,=20server=20messages=20come=20from=20?= =?UTF-8?q?biboumi=E2=80=99s=20hostname=20directly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of irc.example.com@biboumi, because that’s actually user named “irc.example.com”, in that case. And that fixes the raw messages in fixed mode. fix #3286 --- src/bridge/bridge.cpp | 7 +++++-- src/xmpp/biboumi_component.cpp | 5 ++++- src/xmpp/xmpp_component.cpp | 7 ++++++- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 9fd0a5b..b1685e0 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -932,7 +932,10 @@ void Bridge::send_xmpp_message(const std::string& from, const std::string& autho const auto encoding = in_encoding_for(*this, {from, this}); for (const auto& resource: this->resources_in_server[from]) { - this->xmpp.send_message(from, this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat", false, false); + if (Config::get("fixed_irc_server", "").empty()) + this->xmpp.send_message(from, this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat", false, false); + else + this->xmpp.send_message("", this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat", false, false); } } @@ -947,7 +950,7 @@ void Bridge::send_user_join(const std::string& hostname, const std::string& chan const Iid iid(chan_name, hostname, Iid::Type::Channel); this->send_xmpp_invitation(iid, ""); } - else + else { for (const auto& resource: resources) this->send_user_join(hostname, chan_name, user, user_mode, self, resource); diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 0e1d270..6cddeb4 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -281,6 +281,7 @@ void BiboumiComponent::handle_message(const Stanza& stanza) { if (body && !body->get_inner().empty()) { + const auto fixed_irc_server = Config::get("fixed_irc_server", ""); // a message for nick!server if (iid.type == Iid::Type::User && !iid.get_local().empty()) { @@ -296,9 +297,11 @@ void BiboumiComponent::handle_message(const Stanza& stanza) bridge->set_preferred_from_jid(user_iid.get_local(), to_str); } else if (iid.type == Iid::Type::Server) + bridge->send_raw_message(iid.get_server(), body->get_inner()); + else if (iid.type == Iid::Type::None && !fixed_irc_server.empty()) { // Message sent to the server JID // Convert the message body into a raw IRC message - bridge->send_raw_message(iid.get_server(), body->get_inner()); + bridge->send_raw_message(fixed_irc_server, body->get_inner()); } } } diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 42a5392..24a85d7 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -277,7 +277,12 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con if (fulljid) message["from"] = from; else - message["from"] = from + "@" + this->served_hostname; + { + if (!from.empty()) + message["from"] = from + "@" + this->served_hostname; + else + message["from"] = this->served_hostname; + } if (!type.empty()) message["type"] = type; XmlSubNode body_node(message, "body"); -- cgit v1.2.3 From fcaffb9e778ad5962e69dc23c1fc91eb59a27945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 27 Aug 2017 22:30:44 +0200 Subject: Add support for the "history" node on MUC join Supports the "seconds", "maxstanzas", "since" and "maxchars" (but only =0) attributes. fix #3270 --- src/bridge/bridge.cpp | 16 ++++++++++------ src/bridge/bridge.hpp | 7 ++++--- src/bridge/history_limit.hpp | 8 ++++++++ src/irc/irc_client.cpp | 2 +- src/irc/irc_client.hpp | 7 +++++++ src/xmpp/biboumi_component.cpp | 28 +++++++++++++++++++++++++++- 6 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 src/bridge/history_limit.hpp (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index b1685e0..3bc618f 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -167,10 +167,11 @@ IrcClient* Bridge::find_irc_client(const std::string& hostname) const } bool Bridge::join_irc_channel(const Iid& iid, const std::string& nickname, const std::string& password, - const std::string& resource) + const std::string& resource, HistoryLimit history_limit) { const auto& hostname = iid.get_server(); IrcClient* irc = this->make_irc_client(hostname, nickname); + irc->history_limit = history_limit; this->add_resource_to_server(hostname, resource); auto res_in_chan = this->is_resource_in_chan(ChannelKey{iid.get_local(), hostname}, resource); if (!res_in_chan) @@ -993,17 +994,20 @@ void Bridge::send_topic(const std::string& hostname, const std::string& chan_nam } -void Bridge::send_room_history(const std::string& hostname, const std::string& chan_name) +void Bridge::send_room_history(const std::string& hostname, const std::string& chan_name, const HistoryLimit& history_limit) { for (const auto& resource: this->resources_in_chan[ChannelKey{chan_name, hostname}]) - this->send_room_history(hostname, chan_name, resource); + this->send_room_history(hostname, chan_name, resource, history_limit); } -void Bridge::send_room_history(const std::string& hostname, std::string chan_name, const std::string& resource) +void Bridge::send_room_history(const std::string& hostname, std::string chan_name, const std::string& resource, const HistoryLimit& history_limit) { #ifdef USE_DATABASE const auto coptions = Database::get_irc_channel_options_with_server_and_global_default(this->user_jid, hostname, chan_name); - const auto lines = Database::get_muc_logs(this->user_jid, chan_name, hostname, coptions.col()); + auto limit = coptions.col(); + if (history_limit.stanzas >= 0 && history_limit.stanzas < limit) + limit = history_limit.stanzas; + const auto lines = Database::get_muc_logs(this->user_jid, chan_name, hostname, limit, history_limit.since); chan_name.append(utils::empty_if_fixed_server("%" + hostname)); for (const auto& line: lines) { @@ -1257,7 +1261,7 @@ void Bridge::generate_channel_join_for_resource(const Iid& iid, const std::strin this->send_user_join(iid.get_server(), iid.get_encoded_local(), self, self->get_most_significant_mode(irc->get_sorted_user_modes()), true, resource); - this->send_room_history(iid.get_server(), iid.get_local(), resource); + this->send_room_history(iid.get_server(), iid.get_local(), resource, irc->history_limit); this->send_topic(iid.get_server(), iid.get_encoded_local(), channel->topic, channel->topic_author, resource); } diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index c10631b..c2f0233 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -74,7 +75,7 @@ public: * Try to join an irc_channel, does nothing and return true if the channel * was already joined. */ - bool join_irc_channel(const Iid& iid, const std::string& nickname, const std::string& password, const std::string& resource); + bool join_irc_channel(const Iid& iid, const std::string& nickname, const std::string& password, const std::string& resource, HistoryLimit history_limit); void send_channel_message(const Iid& iid, const std::string& body); void send_private_message(const Iid& iid, const std::string& body, const std::string& type="PRIVMSG"); @@ -156,8 +157,8 @@ public: /** * Send the MUC history to the user */ - void send_room_history(const std::string& hostname, const std::string& chan_name); - void send_room_history(const std::string& hostname, std::string chan_name, const std::string& resource); + void send_room_history(const std::string& hostname, const std::string& chan_name, const HistoryLimit& history_limit); + void send_room_history(const std::string& hostname, std::string chan_name, const std::string& resource, const HistoryLimit& history_limit); /** * Send a MUC message from some participant */ diff --git a/src/bridge/history_limit.hpp b/src/bridge/history_limit.hpp new file mode 100644 index 0000000..9c75256 --- /dev/null +++ b/src/bridge/history_limit.hpp @@ -0,0 +1,8 @@ +#pragma once + +// Default values means no limit +struct HistoryLimit +{ + int stanzas{-1}; + std::string since{}; +}; diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 7776c8d..5f26bf0 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -784,7 +784,7 @@ void IrcClient::on_channel_completely_joined(const IrcMessage& message) channel->joined = true; this->bridge.send_user_join(this->hostname, chan_name, channel->get_self(), channel->get_self()->get_most_significant_mode(this->sorted_user_modes), true); - this->bridge.send_room_history(this->hostname, chan_name); + this->bridge.send_room_history(this->hostname, chan_name, this->history_limit); this->bridge.send_topic(this->hostname, chan_name, channel->topic, channel->topic_author); } diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index aec6cd9..de5c520 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include @@ -296,6 +298,11 @@ public: const std::vector& get_sorted_user_modes() const { return this->sorted_user_modes; } std::set get_chantypes() const { return this->chantypes; } + + /** + * Store the history limit that the client asked when joining this room. + */ + HistoryLimit history_limit; private: /** * The hostname of the server we are connected to. diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 6cddeb4..7c5b059 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -24,6 +24,7 @@ #include #include +#include using namespace std::string_literals; @@ -155,8 +156,33 @@ void BiboumiComponent::handle_presence(const Stanza& stanza) bridge->send_irc_nick_change(iid, to.resource, from.resource); const XmlNode* x = stanza.get_child("x", MUC_NS); const XmlNode* password = x ? x->get_child("password", MUC_NS): nullptr; + const XmlNode* history = x ? x->get_child("history", MUC_NS): nullptr; + HistoryLimit history_limit; + if (history) + { + // TODO implement the "seconds" + const auto seconds = history->get_tag("seconds"); + if (!seconds.empty()) + { + const auto now = std::chrono::system_clock::now(); + std::time_t timestamp = std::chrono::system_clock::to_time_t(now); + int int_seconds = std::atoi(seconds.data()); + timestamp -= int_seconds; + history_limit.since = utils::to_string(timestamp); + } + const auto since = history->get_tag("since"); + if (!since.empty()) + history_limit.since = since; + const auto maxstanzas = history->get_tag("maxstanzas"); + if (!maxstanzas.empty()) + history_limit.stanzas = std::atoi(maxstanzas.data()); + // Ignore any other value, because this is too complex to implement, + // so I won’t do it. + if (history->get_tag("maxchars") == "0") + history_limit.stanzas = 0; + } bridge->join_irc_channel(iid, to.resource, password ? password->get_inner(): "", - from.resource); + from.resource, history_limit); } else if (type == "unavailable") { -- cgit v1.2.3 From 98227db6ad005c2e73445ce10e13484cb0568d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 27 Aug 2017 22:41:05 +0200 Subject: Remove a forgotten and useless comment [skip-ci] --- src/xmpp/biboumi_component.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 7c5b059..0b2bba0 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -160,7 +160,6 @@ void BiboumiComponent::handle_presence(const Stanza& stanza) HistoryLimit history_limit; if (history) { - // TODO implement the "seconds" const auto seconds = history->get_tag("seconds"); if (!seconds.empty()) { -- cgit v1.2.3 From dabc48b79b6189c99c246ae01af27fa170fd86a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 30 Aug 2017 16:14:35 +0200 Subject: Mark messages from the IRC server as private and no-copy fix #3284 --- src/bridge/bridge.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 3bc618f..02ba565 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -934,9 +934,9 @@ void Bridge::send_xmpp_message(const std::string& from, const std::string& autho for (const auto& resource: this->resources_in_server[from]) { if (Config::get("fixed_irc_server", "").empty()) - this->xmpp.send_message(from, this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat", false, false); + this->xmpp.send_message(from, this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat", false, true); else - this->xmpp.send_message("", this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat", false, false); + this->xmpp.send_message("", this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource, "chat", false, true); } } -- cgit v1.2.3 From 8d99374f1f02a4d229b49f6697247eb1e1f4f940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 6 Sep 2017 21:30:44 +0200 Subject: When biboumi is logging into journald, use sd_journal_send This makes sure that multi-lines messages are properly parsed as a single message by journald. fix #3268 --- src/logger/logger.cpp | 4 +++ src/logger/logger.hpp | 76 ++++++++++++++++++++++++++------------------------- 2 files changed, 43 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/logger/logger.cpp b/src/logger/logger.cpp index 92a3d9b..4287794 100644 --- a/src/logger/logger.cpp +++ b/src/logger/logger.cpp @@ -7,6 +7,10 @@ Logger::Logger(const int log_level): null_buffer{}, null_stream{&null_buffer} { +#ifdef SYSTEMD_FOUND + if (::getenv("JOURNAL_STREAM") != nullptr && this->use_stdout()) + this->use_systemd = true; +#endif } Logger::Logger(const int log_level, const std::string& log_file): diff --git a/src/logger/logger.hpp b/src/logger/logger.hpp index ff6a82b..176726e 100644 --- a/src/logger/logger.hpp +++ b/src/logger/logger.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #define debug_lvl 0 #define info_lvl 1 @@ -19,7 +20,9 @@ #include "biboumi.h" #ifdef SYSTEMD_FOUND +#define SD_JOURNAL_SUPPRESS_LOCATION # include +# include #else # define SD_DEBUG "[DEBUG]: " # define SD_INFO "[INFO]: " @@ -57,6 +60,15 @@ public: Logger(Logger&&) = delete; Logger& operator=(Logger&&) = delete; +#ifdef SYSTEMD_FOUND + bool use_stdout() const + { + return this->stream.rdbuf() == std::cout.rdbuf(); + } + + bool use_systemd{false}; +#endif + private: const int log_level; std::ofstream ofstream{}; @@ -66,8 +78,6 @@ private: std::ostream null_stream; }; -#define WHERE __FILENAME__, ":", __LINE__, ":\t" - namespace logging_details { template @@ -84,45 +94,37 @@ namespace logging_details } 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) + void do_logging(const int level, int syslog_level, const char* src_file, int line, U&&... args) { - auto& os = Logger::instance()->get_stream(error_lvl); - os << SD_ERR; - log(os, std::forward(args)...); + #ifdef SYSTEMD_FOUND + if (Logger::instance()->use_systemd) + { + (void)level; + std::ostringstream os; + log(os, std::forward(args)...); + sd_journal_send("MESSAGE=%s", os.str().data(), + "PRIORITY=%i", syslog_level, + "CODE_FILE=%s", src_file, + "CODE_LINE=%i", line, + nullptr); + } + else + { + #endif + static const char* priority_names[] = {"DEBUG", "INFO", "WARNING", "ERROR"}; + auto& os = Logger::instance()->get_stream(level); + os << '[' << priority_names[level] << "]: " << src_file << ':' << line << ":\t"; + log(os, std::forward(args)...); +#ifdef SYSTEMD_FOUND + } +#endif } } -#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__) +#define log_debug(...) logging_details::do_logging(debug_lvl, LOG_DEBUG, __FILENAME__, __LINE__, __VA_ARGS__) +#define log_info(...) logging_details::do_logging(info_lvl, LOG_INFO, __FILENAME__, __LINE__, __VA_ARGS__) +#define log_warning(...) logging_details::do_logging(warning_lvl, LOG_WARNING, __FILENAME__, __LINE__, __VA_ARGS__) +#define log_error(...) logging_details::do_logging(error_lvl, LOG_ERR, __FILENAME__, __LINE__, __VA_ARGS__) -- cgit v1.2.3 From bfcf29451787d10c747ad79cb3fd177ac86e9cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 9 Sep 2017 17:09:17 +0200 Subject: Add the persistent_by_default configuration option fix #3293 --- src/bridge/bridge.cpp | 2 +- src/config/config.cpp | 8 ++++++++ src/config/config.hpp | 1 + src/database/database.cpp | 5 +++++ src/database/database.hpp | 5 ++++- src/xmpp/biboumi_adhoc_commands.cpp | 4 ++-- 6 files changed, 21 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 02ba565..8587264 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -435,7 +435,7 @@ void Bridge::leave_irc_channel(Iid&& iid, const std::string& status_message, con bool persistent = false; #ifdef USE_DATABASE const auto goptions = Database::get_global_options(this->user_jid); - if (goptions.col()) + if (goptions.col()) persistent = true; else { diff --git a/src/config/config.cpp b/src/config/config.cpp index 0f3d639..412b170 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -23,6 +23,14 @@ std::string Config::get(const std::string& option, const std::string& def) return it->second; } +bool Config::get_bool(const std::string& option, const bool def) +{ + auto res = Config::get(option, ""); + if (res.empty()) + return def; + return res == "true"; +} + int Config::get_int(const std::string& option, const int& def) { std::string res = Config::get(option, ""); diff --git a/src/config/config.hpp b/src/config/config.hpp index 2ba38cc..c5ef15d 100644 --- a/src/config/config.hpp +++ b/src/config/config.hpp @@ -44,6 +44,7 @@ public: * the second argument as the default. */ static int get_int(const std::string&, const int&); + static bool get_bool(const std::string&, const bool); /** * Set a value for the given option. And write all the config * in the file from which it was read if save is true. diff --git a/src/database/database.cpp b/src/database/database.cpp index f706528..a2b88e2 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -18,6 +20,9 @@ Database::IrcChannelOptionsTable Database::irc_channel_options("IrcChannelOption Database::RosterTable Database::roster("roster"); std::map Database::encoding_in_cache{}; +Database::GlobalPersistent::GlobalPersistent(): + Column{Config::get_bool("persistent_by_default", false)} +{} void Database::open(const std::string& filename) { diff --git a/src/database/database.hpp b/src/database/database.hpp index f4b2ecd..f9695d3 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -73,6 +73,9 @@ class Database struct Persistent: Column { static constexpr auto name = "persistent_"; Persistent(): Column(false) {} }; + struct GlobalPersistent: Column { static constexpr auto name = "persistent_"; + GlobalPersistent(); }; + struct LocalJid: Column { static constexpr auto name = "local"; }; struct RemoteJid: Column { static constexpr auto name = "remote"; }; @@ -81,7 +84,7 @@ class Database using MucLogLineTable = Table; using MucLogLine = MucLogLineTable::RowType; - using GlobalOptionsTable = Table; + using GlobalOptionsTable = Table; using GlobalOptions = GlobalOptionsTable::RowType; using IrcServerOptionsTable = Table; diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp index d78dc98..bcdac39 100644 --- a/src/xmpp/biboumi_adhoc_commands.cpp +++ b/src/xmpp/biboumi_adhoc_commands.cpp @@ -159,7 +159,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman { XmlSubNode value(persistent, "value"); value.set_name("value"); - if (options.col()) + if (options.col()) value.set_inner("true"); else value.set_inner("false"); @@ -193,7 +193,7 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session, } else if (field->get_tag("var") == "persistent" && value) - options.col() = to_bool(value->get_inner()); + options.col() = to_bool(value->get_inner()); } options.save(Database::db); -- cgit v1.2.3 From e217a1978451ca0153fee36618ff4129ffcd2804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 9 Sep 2017 17:21:51 +0200 Subject: Fix the build without systemd, by adding a few define --- src/logger/logger.hpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/logger/logger.hpp b/src/logger/logger.hpp index 176726e..a99648c 100644 --- a/src/logger/logger.hpp +++ b/src/logger/logger.hpp @@ -28,6 +28,10 @@ # define SD_INFO "[INFO]: " # define SD_WARNING "[WARNING]: " # define SD_ERR "[ERROR]: " +# define LOG_ERR 3 +# define LOG_WARNING 4 +# define LOG_INFO 6 +# define LOG_DEBUG 7 #endif // Macro defined to get the filename instead of the full path. But if it is @@ -111,6 +115,7 @@ namespace logging_details else { #endif + (void)syslog_level; static const char* priority_names[] = {"DEBUG", "INFO", "WARNING", "ERROR"}; auto& os = Logger::instance()->get_stream(level); os << '[' << priority_names[level] << "]: " << src_file << ':' << line << ":\t"; -- cgit v1.2.3 From 103e508f3b1c623f687c092790ea0f73d92e65f3 Mon Sep 17 00:00:00 2001 From: "Romain DEP." Date: Thu, 21 Sep 2017 08:38:04 +0200 Subject: compat: revert to using sqlite's close() function for compat with older distros. close_v2(), in use before this commit, was introduced as part of sqlite 3.7.14 (2012-09-03), and is as such incompatible with debian wheezy (3.7.13) and centos6 (3.6.20). FTR, Wheezy will be supported until May 2018, and centos6, until November 2020. --- src/database/database.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/database/database.cpp b/src/database/database.cpp index a2b88e2..8afe6f1 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -35,7 +35,7 @@ void Database::open(const std::string& filename) if (res != SQLITE_OK) { log_error("Failed to open database file ", filename, ": ", sqlite3_errmsg(new_db)); - sqlite3_close_v2(new_db); + sqlite3_close(new_db); throw std::runtime_error(""); } Database::db = new_db; @@ -238,7 +238,7 @@ std::vector Database::get_full_roster() void Database::close() { - sqlite3_close_v2(Database::db); + sqlite3_close(Database::db); Database::db = nullptr; } -- cgit v1.2.3 From 1c3d4f6f5fdf5829e931770523251abe9522914b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 21 Sep 2017 21:57:01 +0200 Subject: Remove a redundant Body definition --- src/bridge/colors.hpp | 12 ++---------- src/xmpp/body.hpp | 4 ++++ 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/bridge/colors.hpp b/src/bridge/colors.hpp index dceed74..25b085a 100644 --- a/src/bridge/colors.hpp +++ b/src/bridge/colors.hpp @@ -6,20 +6,12 @@ * vice versa. */ +#include + #include #include #include -class XmlNode; - -namespace Xmpp -{ -// Contains: -// - an XMPP-valid UTF-8 body -// - an XML node representing the XHTML-IM body, or null - using body = std::tuple>; -} - #define IRC_FORMAT_BOLD_CHAR '\x02' // done #define IRC_FORMAT_COLOR_CHAR '\x03' // done #define IRC_FORMAT_RESET_CHAR '\x0F' // done diff --git a/src/xmpp/body.hpp b/src/xmpp/body.hpp index 068d1a4..f693cdd 100644 --- a/src/xmpp/body.hpp +++ b/src/xmpp/body.hpp @@ -1,5 +1,9 @@ #pragma once +#include +#include + +class XmlNode; namespace Xmpp { -- cgit v1.2.3 From c5a02685361b95042d5c2ada58cba851bc1cc37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 4 Oct 2017 21:25:27 +0200 Subject: Explicitely include all needed botan headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most importantely, include parsing.h, since read_cfg is not implicitely included anymore in botan 2.3, and that does not compile. Also do not included botan.h anymore, since it’s deprecated in botan 2.3 fix #3296 --- src/network/credentials_manager.cpp | 1 + src/network/credentials_manager.hpp | 3 ++- src/network/tcp_socket_handler.cpp | 1 + src/network/tcp_socket_handler.hpp | 1 - src/network/tls_policy.cpp | 2 ++ 5 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/network/credentials_manager.cpp b/src/network/credentials_manager.cpp index 7f07cef..b25f442 100644 --- a/src/network/credentials_manager.cpp +++ b/src/network/credentials_manager.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include /** diff --git a/src/network/credentials_manager.hpp b/src/network/credentials_manager.hpp index aa4732a..3a37bdc 100644 --- a/src/network/credentials_manager.hpp +++ b/src/network/credentials_manager.hpp @@ -4,7 +4,8 @@ #ifdef BOTAN_FOUND -#include +#include +#include #include class TCPSocketHandler; diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index 6239162..343ec56 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -13,6 +13,7 @@ #ifdef BOTAN_FOUND # include +# include # include # include # include diff --git a/src/network/tcp_socket_handler.hpp b/src/network/tcp_socket_handler.hpp index 5cef739..c598641 100644 --- a/src/network/tcp_socket_handler.hpp +++ b/src/network/tcp_socket_handler.hpp @@ -21,7 +21,6 @@ #ifdef BOTAN_FOUND # include -# include # include # include diff --git a/src/network/tls_policy.cpp b/src/network/tls_policy.cpp index 5439397..b88eb88 100644 --- a/src/network/tls_policy.cpp +++ b/src/network/tls_policy.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include bool BiboumiTLSPolicy::load(const std::string& filename) { -- cgit v1.2.3 From f3b1e39cdd7c55c914659f5ac103c3a0855f8f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 26 Oct 2017 19:13:23 +0200 Subject: Add a workaround for https://github.com/randombit/botan/issues/1276 ref #3278 --- src/network/tcp_socket_handler.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index 343ec56..602cf94 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -28,6 +28,8 @@ namespace Botan::TLS::Session_Manager_In_Memory& get_session_manager() { static Botan::TLS::Session_Manager_In_Memory session_manager{get_rng()}; + // workaround for https://github.com/randombit/botan/issues/1276 + session_manager.remove_all(); return session_manager; } } -- cgit v1.2.3 From cb6b50dc0109ee3a201a4012b63f3914cc5ea6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 7 Nov 2017 23:05:05 +0100 Subject: Change how we count the number of connected resources to a server --- src/bridge/bridge.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 8587264..eb1d553 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -894,7 +894,19 @@ void Bridge::send_muc_leave(const Iid& iid, const std::string& nick, this->xmpp.send_muc_leave(std::to_string(iid), nick, this->make_xmpp_body(message), this->user_jid + "/" + res, self, user_requested); if (self) - this->remove_all_resources_from_chan(iid.to_tuple()); + { + // Copy the resources currently in that channel + const auto resources_in_chan = this->resources_in_chan[iid.to_tuple()]; + + this->remove_all_resources_from_chan(iid.to_tuple()); + + // Now, for each resource that was in that channel, remove it from the server if it’s + // not in any other channel + for (const auto& r: resources_in_chan) + if (this->number_of_channels_the_resource_is_in(iid.get_server(), r) == 0) + this->remove_resource_from_server(iid.get_server(), r); + + } } IrcClient* irc = this->find_irc_client(iid.get_server()); @@ -1236,9 +1248,13 @@ std::size_t Bridge::number_of_channels_the_resource_is_in(const std::string& irc std::size_t res = 0; for (auto pair: this->resources_in_chan) { - if (std::get<0>(pair.first) == irc_hostname && pair.second.count(resource) != 0) + if (std::get<1>(pair.first) == irc_hostname && pair.second.count(resource) != 0) res++; } + + IrcClient* irc = this->find_irc_client(irc_hostname); + if (irc && (irc->get_dummy_channel().joined || irc->get_dummy_channel().joining)) + res++; return res; } -- cgit v1.2.3 From 90dd8e8b422efdb1fc6c1fb6bfcbc423a4f700a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 7 Nov 2017 23:06:04 +0100 Subject: Fix #3304 --- src/bridge/bridge.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index eb1d553..925b226 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -468,9 +468,9 @@ void Bridge::leave_irc_channel(Iid&& iid, const std::string& status_message, con "Biboumi note: " + std::to_string(resources - 1) + " resources are still in this channel.", true, true, resource); this->remove_resource_from_chan(key, resource); + } if (this->number_of_channels_the_resource_is_in(iid.get_server(), resource) == 0) this->remove_resource_from_server(iid.get_server(), resource); - } } -- cgit v1.2.3 From 5b27cee97272d4ae6ff30f03dc57221fa3e3183f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 27 Nov 2017 00:48:40 +0100 Subject: Send \r\n at the end of the identd responses fix #3315 --- src/identd/identd_socket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/identd/identd_socket.cpp b/src/identd/identd_socket.cpp index b85257c..92cd80b 100644 --- a/src/identd/identd_socket.cpp +++ b/src/identd/identd_socket.cpp @@ -50,14 +50,14 @@ std::string IdentdSocket::generate_answer(const BiboumiComponent& biboumi, uint1 if (pair.second->match_port_pairt(local, remote)) { std::ostringstream os; - os << local << " , " << remote << " : USERID : OTHER : " << hash_jid(bridge->get_bare_jid()); + os << local << " , " << remote << " : USERID : OTHER : " << hash_jid(bridge->get_bare_jid()) << "\r\n"; log_debug("Identd, sending: ", os.str()); return os.str(); } } } std::ostringstream os; - os << local << " , " << remote << " ERROR : NO-USER"; + os << local << " , " << remote << " ERROR : NO-USER" << "\r\n"; log_debug("Identd, sending: ", os.str()); return os.str(); } -- cgit v1.2.3 From 0168b96b79db2627fdba77a8712956408aa081d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 4 Oct 2017 21:28:18 +0200 Subject: Add postgresql support --- src/database/column.hpp | 9 ++- src/database/count_query.hpp | 12 ++-- src/database/database.cpp | 75 ++++++++++---------- src/database/database.hpp | 33 +++++---- src/database/engine.hpp | 40 +++++++++++ src/database/index.hpp | 22 +++--- src/database/insert_query.hpp | 103 ++++++++++++++------------- src/database/postgresql_engine.cpp | 77 ++++++++++++++++++++ src/database/postgresql_engine.hpp | 31 ++++++++ src/database/postgresql_statement.hpp | 128 ++++++++++++++++++++++++++++++++++ src/database/query.cpp | 29 ++++++-- src/database/query.hpp | 39 ++--------- src/database/row.hpp | 105 ++++++++++++++-------------- src/database/select_query.hpp | 24 ++++--- src/database/sqlite3_engine.cpp | 93 ++++++++++++++++++++++++ src/database/sqlite3_engine.hpp | 30 ++++++++ src/database/sqlite3_statement.hpp | 93 ++++++++++++++++++++++++ src/database/statement.hpp | 45 +++++------- src/database/table.cpp | 23 ------ src/database/table.hpp | 89 ++++++++++++----------- src/database/type_to_sql.cpp | 9 --- src/database/type_to_sql.hpp | 16 ----- src/database/update_query.hpp | 100 ++++++++++++++++++++++++++ src/main.cpp | 2 + src/utils/is_one_of.hpp | 17 +++++ src/utils/optional_bool.cpp | 8 +++ src/utils/optional_bool.hpp | 4 +- 27 files changed, 922 insertions(+), 334 deletions(-) create mode 100644 src/database/engine.hpp create mode 100644 src/database/postgresql_engine.cpp create mode 100644 src/database/postgresql_engine.hpp create mode 100644 src/database/postgresql_statement.hpp create mode 100644 src/database/sqlite3_engine.cpp create mode 100644 src/database/sqlite3_engine.hpp create mode 100644 src/database/sqlite3_statement.hpp delete mode 100644 src/database/table.cpp delete mode 100644 src/database/type_to_sql.cpp delete mode 100644 src/database/type_to_sql.hpp create mode 100644 src/database/update_query.hpp create mode 100644 src/utils/is_one_of.hpp create mode 100644 src/utils/optional_bool.cpp (limited to 'src') diff --git a/src/database/column.hpp b/src/database/column.hpp index 111f9ca..1f16bcf 100644 --- a/src/database/column.hpp +++ b/src/database/column.hpp @@ -13,5 +13,10 @@ struct Column T value{}; }; -struct Id: Column { static constexpr auto name = "id_"; - static constexpr auto options = "PRIMARY KEY AUTOINCREMENT"; }; +struct Id: Column { + static constexpr std::size_t unset_value = static_cast(-1); + static constexpr auto name = "id_"; + static constexpr auto options = "PRIMARY KEY"; + + Id(): Column(-1) {} +}; diff --git a/src/database/count_query.hpp b/src/database/count_query.hpp index 0dde63c..b462e0f 100644 --- a/src/database/count_query.hpp +++ b/src/database/count_query.hpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -15,20 +16,17 @@ struct CountQuery: public Query this->body += std::move(name); } - int64_t execute(sqlite3* db) + int64_t execute(DatabaseEngine& db) { - auto statement = this->prepare(db); + auto statement = db.prepare(this->body); int64_t res = 0; - if (sqlite3_step(statement.get()) == SQLITE_ROW) - res = sqlite3_column_int64(statement.get(), 0); + if (statement->step() != StepResult::Error) + res = statement->get_column_int64(0); else { log_error("Count request didn’t return a result"); return 0; } - if (sqlite3_step(statement.get()) != SQLITE_DONE) - log_warning("Count request returned more than one result."); - return res; } }; diff --git a/src/database/database.cpp b/src/database/database.cpp index 8afe6f1..b1a5ba5 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -7,16 +7,21 @@ #include #include +#include +#include +#include #include +#include + #include -sqlite3* Database::db; -Database::MucLogLineTable Database::muc_log_lines("MucLogLine_"); -Database::GlobalOptionsTable Database::global_options("GlobalOptions_"); -Database::IrcServerOptionsTable Database::irc_server_options("IrcServerOptions_"); -Database::IrcChannelOptionsTable Database::irc_channel_options("IrcChannelOptions_"); +std::unique_ptr Database::db; +Database::MucLogLineTable Database::muc_log_lines("muclogline_"); +Database::GlobalOptionsTable Database::global_options("globaloptions_"); +Database::IrcServerOptionsTable Database::irc_server_options("ircserveroptions_"); +Database::IrcChannelOptionsTable Database::irc_channel_options("ircchanneloptions_"); Database::RosterTable Database::roster("roster"); std::map Database::encoding_in_cache{}; @@ -29,27 +34,26 @@ void Database::open(const std::string& filename) // Try to open the specified database. // Close and replace the previous database pointer if it succeeded. If it did // not, just leave things untouched - sqlite3* new_db; - auto res = sqlite3_open_v2(filename.data(), &new_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); - Database::close(); - if (res != SQLITE_OK) - { - log_error("Failed to open database file ", filename, ": ", sqlite3_errmsg(new_db)); - sqlite3_close(new_db); - throw std::runtime_error(""); - } - Database::db = new_db; - Database::muc_log_lines.create(Database::db); - Database::muc_log_lines.upgrade(Database::db); - Database::global_options.create(Database::db); - Database::global_options.upgrade(Database::db); - Database::irc_server_options.create(Database::db); - Database::irc_server_options.upgrade(Database::db); - Database::irc_channel_options.create(Database::db); - Database::irc_channel_options.upgrade(Database::db); - Database::roster.create(Database::db); - Database::roster.upgrade(Database::db); - create_index(Database::db, "archive_index", Database::muc_log_lines.get_name()); + std::unique_ptr new_db; + static const auto psql_prefix = "postgresql://"s; + if (filename.substr(0, psql_prefix.size()) == psql_prefix) + new_db = PostgresqlEngine::open("dbname="s + filename.substr(psql_prefix.size())); + else + new_db = Sqlite3Engine::open(filename); + if (!new_db) + return; + Database::db = std::move(new_db); + Database::muc_log_lines.create(*Database::db); + Database::muc_log_lines.upgrade(*Database::db); + Database::global_options.create(*Database::db); + Database::global_options.upgrade(*Database::db); + Database::irc_server_options.create(*Database::db); + Database::irc_server_options.upgrade(*Database::db); + Database::irc_channel_options.create(*Database::db); + Database::irc_channel_options.upgrade(*Database::db); + Database::roster.create(*Database::db); + Database::roster.upgrade(*Database::db); + create_index(*Database::db, "archive_index", Database::muc_log_lines.get_name()); } @@ -59,7 +63,7 @@ Database::GlobalOptions Database::get_global_options(const std::string& owner) request.where() << Owner{} << "=" << owner; Database::GlobalOptions options{Database::global_options.get_name()}; - auto result = request.execute(Database::db); + auto result = request.execute(*Database::db); if (result.size() == 1) options = result.front(); else @@ -73,7 +77,7 @@ Database::IrcServerOptions Database::get_irc_server_options(const std::string& o request.where() << Owner{} << "=" << owner << " and " << Server{} << "=" << server; Database::IrcServerOptions options{Database::irc_server_options.get_name()}; - auto result = request.execute(Database::db); + auto result = request.execute(*Database::db); if (result.size() == 1) options = result.front(); else @@ -91,7 +95,7 @@ Database::IrcChannelOptions Database::get_irc_channel_options(const std::string& " and " << Server{} << "=" << server <<\ " and " << Channel{} << "=" << channel; Database::IrcChannelOptions options{Database::irc_channel_options.get_name()}; - auto result = request.execute(Database::db); + auto result = request.execute(*Database::db); if (result.size() == 1) options = result.front(); else @@ -186,7 +190,7 @@ std::vector Database::get_muc_logs(const std::string& owne if (limit >= 0) request.limit() << limit; - auto result = request.execute(Database::db); + auto result = request.execute(*Database::db); return {result.crbegin(), result.crend()}; } @@ -207,7 +211,7 @@ void Database::delete_roster_item(const std::string& local, const std::string& r query << " WHERE " << Database::RemoteJid{} << "=" << remote << \ " AND " << Database::LocalJid{} << "=" << local; - query.execute(Database::db); +// query.execute(*Database::db); } bool Database::has_roster_item(const std::string& local, const std::string& remote) @@ -216,7 +220,7 @@ bool Database::has_roster_item(const std::string& local, const std::string& remo query.where() << Database::LocalJid{} << "=" << local << \ " and " << Database::RemoteJid{} << "=" << remote; - auto res = query.execute(Database::db); + auto res = query.execute(*Database::db); return !res.empty(); } @@ -226,20 +230,19 @@ std::vector Database::get_contact_list(const std::string& auto query = Database::roster.select(); query.where() << Database::LocalJid{} << "=" << local; - return query.execute(Database::db); + return query.execute(*Database::db); } std::vector Database::get_full_roster() { auto query = Database::roster.select(); - return query.execute(Database::db); + return query.execute(*Database::db); } void Database::close() { - sqlite3_close(Database::db); - Database::db = nullptr; + Database::db.release(); } std::string Database::gen_uuid() diff --git a/src/database/database.hpp b/src/database/database.hpp index f9695d3..ec44543 100644 --- a/src/database/database.hpp +++ b/src/database/database.hpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include @@ -25,11 +27,11 @@ class Database struct Owner: Column { static constexpr auto name = "owner_"; }; - struct IrcChanName: Column { static constexpr auto name = "ircChanName_"; }; + struct IrcChanName: Column { static constexpr auto name = "ircchanname_"; }; struct Channel: Column { static constexpr auto name = "channel_"; }; - struct IrcServerName: Column { static constexpr auto name = "ircServerName_"; }; + struct IrcServerName: Column { static constexpr auto name = "ircservername_"; }; struct Server: Column { static constexpr auto name = "server_"; }; @@ -44,30 +46,30 @@ class Database struct Ports: Column { static constexpr auto name = "ports_"; Ports(): Column("6667") {} }; - struct TlsPorts: Column { static constexpr auto name = "tlsPorts_"; + struct TlsPorts: Column { static constexpr auto name = "tlsports_"; TlsPorts(): Column("6697;6670") {} }; struct Username: Column { static constexpr auto name = "username_"; }; struct Realname: Column { static constexpr auto name = "realname_"; }; - struct AfterConnectionCommand: Column { static constexpr auto name = "afterConnectionCommand_"; }; + struct AfterConnectionCommand: Column { static constexpr auto name = "afterconnectioncommand_"; }; - struct TrustedFingerprint: Column { static constexpr auto name = "trustedFingerprint_"; }; + struct TrustedFingerprint: Column { static constexpr auto name = "trustedfingerprint_"; }; - struct EncodingOut: Column { static constexpr auto name = "encodingOut_"; }; + struct EncodingOut: Column { static constexpr auto name = "encodingout_"; }; - struct EncodingIn: Column { static constexpr auto name = "encodingIn_"; }; + struct EncodingIn: Column { static constexpr auto name = "encodingin_"; }; - struct MaxHistoryLength: Column { static constexpr auto name = "maxHistoryLength_"; + struct MaxHistoryLength: Column { static constexpr auto name = "maxhistorylength_"; MaxHistoryLength(): Column(20) {} }; - struct RecordHistory: Column { static constexpr auto name = "recordHistory_"; + struct RecordHistory: Column { static constexpr auto name = "recordhistory_"; RecordHistory(): Column(true) {}}; - struct RecordHistoryOptional: Column { static constexpr auto name = "recordHistory_"; }; + struct RecordHistoryOptional: Column { static constexpr auto name = "recordhistory_"; }; - struct VerifyCert: Column { static constexpr auto name = "verifyCert_"; + struct VerifyCert: Column { static constexpr auto name = "verifycert_"; VerifyCert(): Column(true) {} }; struct Persistent: Column { static constexpr auto name = "persistent_"; @@ -134,7 +136,7 @@ class Database static int64_t count(const TableType& table) { CountQuery query{table.get_name()}; - return query.execute(Database::db); + return query.execute(*Database::db); } static MucLogLineTable muc_log_lines; @@ -142,7 +144,7 @@ class Database static IrcServerOptionsTable irc_server_options; static IrcChannelOptionsTable irc_channel_options; static RosterTable roster; - static sqlite3* db; + static std::unique_ptr db; /** * Some caches, to avoid doing very frequent query requests for a few options. @@ -177,6 +179,11 @@ class Database Database::encoding_in_cache.clear(); } + static auto raw_exec(const std::string& query) + { + Database::db->raw_exec(query); + } + private: static std::string gen_uuid(); static std::map encoding_in_cache; diff --git a/src/database/engine.hpp b/src/database/engine.hpp new file mode 100644 index 0000000..2dd4c21 --- /dev/null +++ b/src/database/engine.hpp @@ -0,0 +1,40 @@ +#pragma once + +/** + * Interface to provide non-portable behaviour, specific to each + * database engine we want to support. + * + * Everything else (all portable stuf) should go outside of this class. + */ + +#include + +#include +#include +#include +#include +#include + +class DatabaseEngine +{ + public: + + DatabaseEngine() = default; + + DatabaseEngine(const DatabaseEngine&) = delete; + DatabaseEngine& operator=(const DatabaseEngine&) = delete; + DatabaseEngine(DatabaseEngine&&) = delete; + DatabaseEngine& operator=(DatabaseEngine&&) = delete; + + virtual std::set get_all_columns_from_table(const std::string& table_name) = 0; + virtual std::tuple raw_exec(const std::string& query) = 0; + virtual std::unique_ptr prepare(const std::string& query) = 0; + virtual void extract_last_insert_rowid(Statement& statement) = 0; + virtual std::string get_returning_id_sql_string(const std::string&) + { + return {}; + } + virtual std::string id_column_type() = 0; + + int64_t last_inserted_rowid{-1}; +}; diff --git a/src/database/index.hpp b/src/database/index.hpp index 5924779..30766ab 100644 --- a/src/database/index.hpp +++ b/src/database/index.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -25,18 +25,14 @@ add_column_name(std::string& out) } template -void create_index(sqlite3* db, const std::string& name, const std::string& table) +void create_index(DatabaseEngine& db, const std::string& name, const std::string& table) { - std::string res{"CREATE INDEX IF NOT EXISTS "}; - res += name + " ON " + table + "("; - add_column_name<0, Columns...>(res); - res += ")"; + std::string query{"CREATE INDEX IF NOT EXISTS "}; + query += name + " ON " + table + "("; + add_column_name<0, Columns...>(query); + query += ")"; - char* error; - const auto result = sqlite3_exec(db, res.data(), nullptr, nullptr, &error); - if (result != SQLITE_OK) - { - log_error("Error executing query: ", error); - sqlite3_free(error); - } + auto result = db.raw_exec(query); + if (std::get<0>(result) == false) + log_error("Error executing query: ", std::get<1>(result)); } diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 2ece69d..2fc0512 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -12,62 +12,63 @@ #include -template -typename std::enable_if, Id>::value, void>::type -actual_bind(Statement& statement, std::vector& params, const std::tuple&) +template +typename std::enable_if::type +update_autoincrement_id(std::tuple& columns, Statement& statement) { - const auto value = params.front(); - params.erase(params.begin()); - if (sqlite3_bind_text(statement.get(), N + 1, value.data(), static_cast(value.size()), SQLITE_TRANSIENT) != SQLITE_OK) - log_error("Failed to bind ", value, " to param ", N); -} - -template -typename std::enable_if, Id>::value, void>::type -actual_bind(Statement& statement, std::vector&, const std::tuple& columns) -{ - auto&& column = std::get(columns); - if (column.value != 0) + using ColumnType = typename std::decay(columns))>::type; + if (std::is_same::value) { - if (sqlite3_bind_int64(statement.get(), N + 1, static_cast(column.value)) != SQLITE_OK) - log_error("Failed to bind ", column.value, " to id."); + log_debug("EXTRACTING LAST ID"); + auto&& column = std::get(columns); } - else if (sqlite3_bind_null(statement.get(), N + 1) != SQLITE_OK) - log_error("Failed to bind NULL to param ", N); + update_autoincrement_id(columns, statement); } +template +typename std::enable_if::type +update_autoincrement_id(std::tuple&, Statement& statement) +{} + struct InsertQuery: public Query { - InsertQuery(const std::string& name): - Query("INSERT OR REPLACE INTO ") + template + InsertQuery(const std::string& name, const std::tuple& columns): + Query("INSERT INTO ") { this->body += name; + this->insert_col_names(columns); + this->insert_values(columns); } template - void execute(const std::tuple& columns, sqlite3* db) + void execute(DatabaseEngine& db, std::tuple& columns) { - auto statement = this->prepare(db); - { - this->bind_param(columns, statement); - if (sqlite3_step(statement.get()) != SQLITE_DONE) - log_error("Failed to execute query: ", sqlite3_errmsg(db)); - } + auto statement = db.prepare(this->body); + this->bind_param(columns, *statement); + + if (statement->step() != StepResult::Error) + db.extract_last_insert_rowid(*statement); + else + log_error("Failed to extract the rowid from the last INSERT"); } template typename std::enable_if::type - bind_param(const std::tuple& columns, Statement& statement) + bind_param(const std::tuple& columns, Statement& statement, int index=1) { - using ColumnType = typename std::remove_reference(columns))>::type; + auto&& column = std::get(columns); + using ColumnType = std::decay_t; + + if (!std::is_same::value) + actual_bind(statement, column.value, index++); - actual_bind(statement, this->params, columns); - this->bind_param(columns, statement); + this->bind_param(columns, statement, index); } template typename std::enable_if::type - bind_param(const std::tuple&, Statement&) + bind_param(const std::tuple&, Statement&, int) {} template @@ -80,18 +81,21 @@ struct InsertQuery: public Query template typename std::enable_if::type - insert_value(const std::tuple& columns) + insert_value(const std::tuple& columns, int index=1) { - this->body += "?"; - if (N != sizeof...(T) - 1) - this->body += ","; - this->body += " "; - add_param(*this, std::get(columns)); - this->insert_value(columns); + using ColumnType = std::decay_t(columns))>; + + if (!std::is_same::value) + { + this->body += "$" + std::to_string(index++); + if (N != sizeof...(T) - 1) + this->body += ", "; + } + this->insert_value(columns, index); } template typename std::enable_if::type - insert_value(const std::tuple&) + insert_value(const std::tuple&, const int) { } template @@ -99,27 +103,28 @@ struct InsertQuery: public Query { this->body += " ("; this->insert_col_name(columns); - this->body += ")\n"; + this->body += ")"; } template typename std::enable_if::type insert_col_name(const std::tuple& columns) { - using ColumnType = typename std::remove_reference(columns))>::type; + using ColumnType = std::decay_t(columns))>; - this->body += ColumnType::name; + if (!std::is_same::value) + { + this->body += ColumnType::name; - if (N < (sizeof...(T) - 1)) - this->body += ", "; + if (N < (sizeof...(T) - 1)) + this->body += ", "; + } this->insert_col_name(columns); } + template typename std::enable_if::type insert_col_name(const std::tuple&) {} - - - private: }; diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp new file mode 100644 index 0000000..9f603ab --- /dev/null +++ b/src/database/postgresql_engine.cpp @@ -0,0 +1,77 @@ +#include + +#include + +#include + +PostgresqlEngine::PostgresqlEngine(PGconn*const conn): + conn(conn) +{} + +PostgresqlEngine::~PostgresqlEngine() +{ + PQfinish(this->conn); +} + +std::unique_ptr PostgresqlEngine::open(const std::string& conninfo) +{ + log_debug("trying to open: ", conninfo); + PGconn* con = PQconnectdb(conninfo.data()); + + if (!con) + { + log_error("Failed to allocate a Postgresql connection"); + throw std::runtime_error(""); + } + const auto status = PQstatus(con); + if (status != CONNECTION_OK) + { + const char* errmsg = PQerrorMessage(con); + log_error("Postgresql connection failed: ", errmsg); + throw std::runtime_error("failed to open connection."); + } + return std::make_unique(con); +} + +std::set PostgresqlEngine::get_all_columns_from_table(const std::string& table_name) +{ + const auto query = "SELECT column_name from information_schema.columns where table_name='" + table_name + "'"; + auto statement = this->prepare(query); + std::set columns; + + while (statement->step() == StepResult::Row) + columns.insert(statement->get_column_text(0)); + + log_debug("found ", columns.size(), " columns."); + return columns; +} + +std::tuple PostgresqlEngine::raw_exec(const std::string& query) +{ + log_debug("raw_exec:", query); + PGresult* res = PQexec(this->conn, query.data()); + auto res_status = PQresultStatus(res); + if (res_status != PGRES_COMMAND_OK) + return std::make_tuple(false, PQresultErrorMessage(res)); + return std::make_tuple(true, std::string{}); +} + +std::unique_ptr PostgresqlEngine::prepare(const std::string& query) +{ + return std::make_unique(query, this->conn); +} + +void PostgresqlEngine::extract_last_insert_rowid(Statement& statement) +{ + this->last_inserted_rowid = statement.get_column_int64(0); +} + +std::string PostgresqlEngine::get_returning_id_sql_string(const std::string& col_name) +{ + return " RETURNING " + col_name; +} + +std::string PostgresqlEngine::id_column_type() +{ + return "SERIAL"; +} diff --git a/src/database/postgresql_engine.hpp b/src/database/postgresql_engine.hpp new file mode 100644 index 0000000..e6444d4 --- /dev/null +++ b/src/database/postgresql_engine.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include + +#include + +#include +#include +#include +#include + +class PostgresqlEngine: public DatabaseEngine +{ + public: + PostgresqlEngine(PGconn*const conn); + + ~PostgresqlEngine(); + + static std::unique_ptr open(const std::string& string); + + std::set get_all_columns_from_table(const std::string& table_name) override final; + std::tuple raw_exec(const std::string& query) override final; + std::unique_ptr prepare(const std::string& query) override; + void extract_last_insert_rowid(Statement& statement) override; + std::string get_returning_id_sql_string(const std::string& col_name) override; + std::string id_column_type() override; +private: + PGconn* const conn; +}; diff --git a/src/database/postgresql_statement.hpp b/src/database/postgresql_statement.hpp new file mode 100644 index 0000000..5016350 --- /dev/null +++ b/src/database/postgresql_statement.hpp @@ -0,0 +1,128 @@ +#pragma once + +#include + +#include + +#include + +class PostgresqlStatement: public Statement +{ + public: + PostgresqlStatement(std::string body, PGconn*const conn): + body(std::move(body)), + conn(conn) + {} + ~PostgresqlStatement() + {} + PostgresqlStatement(const PostgresqlStatement&) = delete; + PostgresqlStatement& operator=(const PostgresqlStatement&) = delete; + PostgresqlStatement(PostgresqlStatement&& other) = delete; + PostgresqlStatement& operator=(PostgresqlStatement&& other) = delete; + + StepResult step() override final + { + if (!this->executed) + { + this->current_tuple = 0; + this->executed = true; + if (!this->execute()) + return StepResult::Error; + } + else + { + this->current_tuple++; + } + if (this->current_tuple < PQntuples(this->result)) + return StepResult::Row; + return StepResult::Done; + } + + int64_t get_column_int64(const int col) override + { + const char* result = PQgetvalue(this->result, this->current_tuple, col); + std::istringstream iss; + iss.str(result); + int64_t res; + iss >> res; + return res; + } + std::string get_column_text(const int col) override + { + const char* result = PQgetvalue(this->result, this->current_tuple, col); + return result; + } + int get_column_int(const int col) override + { + const char* result = PQgetvalue(this->result, this->current_tuple, col); + std::istringstream iss; + iss.str(result); + int res; + iss >> res; + return res; + } + + void bind(std::vector params) override + { + + this->params = std::move(params); + } + + bool bind_text(const int, const std::string& data) override + { + this->params.push_back(data); + return true; + } + bool bind_int64(const int pos, const std::int64_t value) override + { + this->params.push_back(std::to_string(value)); + return true; + } + bool bind_null(const int pos) override + { + this->params.push_back("NULL"); + return true; + } + + private: + +private: + bool execute() + { + std::vector params; + params.reserve(this->params.size()); + + for (const auto& param: this->params) + { + log_debug("param:", param); + params.push_back(param.data()); + } + + log_debug("body: ", body); + this->result = PQexecParams(this->conn, this->body.data(), + this->params.size(), + nullptr, + params.data(), + nullptr, + nullptr, + 0); + const auto status = PQresultStatus(this->result); + if (status == PGRES_TUPLES_OK) + { + log_debug("PGRES_TUPLES_OK"); + } + else if (status != PGRES_COMMAND_OK) + { + log_error("Failed to execute command: ", PQresultErrorMessage(this->result)); + return false; + } + return true; + } + + bool executed{false}; + std::string body; + PGconn*const conn; + std::vector params; + PGresult* result{nullptr}; + int current_tuple{0}; +}; diff --git a/src/database/query.cpp b/src/database/query.cpp index ba63a92..6f305b2 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -1,9 +1,29 @@ #include #include -template <> -void add_param(Query&, const Id&) -{} +void actual_bind(Statement& statement, const std::string& value, int index) +{ + log_debug("binding string:", value, " to col ", index); + statement.bind_text(index, value); +} + +void actual_bind(Statement& statement, const std::size_t value, int index) +{ + log_debug("binding size_t:", value); + statement.bind_int64(index, value); +} + +void actual_bind(Statement& statement, const OptionalBool& value, int index) +{ + log_debug("binding optional_t:", value.to_string()); + if (!value.is_set) + statement.bind_int64(index, 0); + else if (value.value) + statement.bind_int64(index, 1); + else + statement.bind_int64(index, -1); +} + void actual_add_param(Query& query, const std::string& val) { @@ -28,7 +48,8 @@ Query& operator<<(Query& query, const char* str) Query& operator<<(Query& query, const std::string& str) { - query.body += "?"; + query.body += "$" + std::to_string(query.current_param); + query.current_param++; actual_add_param(query, str); return query; } diff --git a/src/database/query.hpp b/src/database/query.hpp index 6e1db12..2467749 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -11,52 +11,27 @@ #include +void actual_bind(Statement& statement, const std::string& value, int index); +void actual_bind(Statement& statement, const std::size_t value, int index); +void actual_bind(Statement& statement, const OptionalBool& value, int index); + struct Query { std::string body; std::vector params; + int current_param{1}; Query(std::string str): body(std::move(str)) {} - - Statement prepare(sqlite3* db) - { - sqlite3_stmt* stmt; - auto res = sqlite3_prepare(db, this->body.data(), static_cast(this->body.size()) + 1, - &stmt, nullptr); - if (res != SQLITE_OK) - { - log_error("Error preparing statement: ", sqlite3_errmsg(db)); - return nullptr; - } - Statement statement(stmt); - int i = 1; - for (const std::string& param: this->params) - { - if (sqlite3_bind_text(statement.get(), i, param.data(), static_cast(param.size()), SQLITE_TRANSIENT) != SQLITE_OK) - log_error("Failed to bind ", param, " to param ", i); - i++; - } - - return statement; - } - - void execute(sqlite3* db) - { - auto statement = this->prepare(db); - while (sqlite3_step(statement.get()) != SQLITE_DONE) - ; - } }; template void add_param(Query& query, const ColumnType& column) { + std::cout << "add_param" << std::endl; actual_add_param(query, column.value); } -template <> -void add_param(Query& query, const Id& column); template void actual_add_param(Query& query, const T& val) @@ -81,7 +56,7 @@ template typename std::enable_if::value, Query&>::type operator<<(Query& query, const Integer& i) { - query.body += "?"; + query.body += "$" + std::to_string(query.current_param++); actual_add_param(query, i); return query; } diff --git a/src/database/row.hpp b/src/database/row.hpp index 2b50874..b69fac5 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -1,72 +1,73 @@ #pragma once #include +#include #include +#include + #include #include -template -typename std::enable_if, Id>::value, void>::type -update_id(std::tuple&, sqlite3*) -{} - -template -typename std::enable_if, Id>::value, void>::type -update_id(std::tuple& columns, sqlite3* db) +template +struct Row { - auto&& column = std::get(columns); - auto res = sqlite3_last_insert_rowid(db); - column.value = static_cast(res); -} + Row(std::string name): + table_name(std::move(name)) + {} -template -typename std::enable_if::type -update_autoincrement_id(std::tuple& columns, sqlite3* db) -{ - using ColumnType = typename std::remove_reference(columns))>::type; - update_id(columns, db); - update_autoincrement_id(columns, db); -} + template + typename Type::real_type& col() + { + auto&& col = std::get(this->columns); + return col.value; + } -template -typename std::enable_if::type -update_autoincrement_id(std::tuple&, sqlite3*) -{} + template + const auto& col() const + { + auto&& col = std::get(this->columns); + return col.value; + } -template -struct Row -{ - Row(std::string name): - table_name(std::move(name)) - {} + template + void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) + { + this->insert(*db); + } - template - typename Type::real_type& col() - { - auto&& col = std::get(this->columns); - return col.value; - } + template + void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) + { + const Id& id = std::get(this->columns); + if (id.value == Id::unset_value) + { + this->insert(*db); + std::get(this->columns).value = db->last_inserted_rowid; + } + else + this->update(*db); + } - template - const auto& col() const - { - auto&& col = std::get(this->columns); - return col.value; - } + private: + void insert(DatabaseEngine& db) + { + InsertQuery query(this->table_name, this->columns); + // Ugly workaround for non portable stuff + query.body += db.get_returning_id_sql_string(Id::name); + query.execute(db, this->columns); + } - void save(sqlite3* db) - { - InsertQuery query(this->table_name); - query.insert_col_names(this->columns); - query.insert_values(this->columns); + void update(DatabaseEngine& db) + { + UpdateQuery query(this->table_name, this->columns); - query.execute(this->columns, db); + query.execute(db, this->columns); + } - update_autoincrement_id(this->columns, db); - } +public: + std::tuple columns; + std::string table_name; - std::tuple columns; - std::string table_name; }; diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 872001c..22b342e 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -18,24 +20,21 @@ template typename std::enable_if::value, sqlite3_int64>::type extract_row_value(Statement& statement, const int i) { - return sqlite3_column_int64(statement.get(), i); + return statement.get_column_int64(i); } template typename std::enable_if::value, T>::type extract_row_value(Statement& statement, const int i) { - const auto size = sqlite3_column_bytes(statement.get(), i); - const unsigned char* str = sqlite3_column_text(statement.get(), i); - std::string result(reinterpret_cast(str), static_cast(size)); - return result; + return statement.get_column_text(i); } template typename std::enable_if::value, T>::type extract_row_value(Statement& statement, const int i) { - const auto integer = sqlite3_column_int(statement.get(), i); + const auto integer = statement.get_column_int(i); OptionalBool result; if (integer > 0) result.set_value(true); @@ -109,16 +108,21 @@ struct SelectQuery: public Query return *this; } - auto execute(sqlite3* db) + auto execute(DatabaseEngine& db) { - auto statement = this->prepare(db); std::vector> rows; - while (sqlite3_step(statement.get()) == SQLITE_ROW) + + auto statement = db.prepare(this->body); + statement->bind(std::move(this->params)); + + while (statement->step() == StepResult::Row) { + log_debug("one result."); Row row(this->table_name); - extract_row_values(row, statement); + extract_row_values(row, *statement); rows.push_back(row); } + return rows; } diff --git a/src/database/sqlite3_engine.cpp b/src/database/sqlite3_engine.cpp new file mode 100644 index 0000000..7e34f89 --- /dev/null +++ b/src/database/sqlite3_engine.cpp @@ -0,0 +1,93 @@ +#include + +#include + +#include +#include +#include + +Sqlite3Engine::Sqlite3Engine(sqlite3* db): + db(db) +{ +} + +Sqlite3Engine::~Sqlite3Engine() +{ + sqlite3_close(this->db); +} + +std::set Sqlite3Engine::get_all_columns_from_table(const std::string& table_name) +{ + std::set result; + char* errmsg; + std::string query{"PRAGMA table_info(" + table_name + ")"}; + int res = sqlite3_exec(this->db, query.data(), [](void* param, int columns_nb, char** columns, char**) -> int { + constexpr int name_column = 1; + std::set* result = static_cast*>(param); + if (name_column < columns_nb) + result->insert(utils::tolower(columns[name_column])); + return 0; + }, &result, &errmsg); + + if (res != SQLITE_OK) + { + log_error("Error executing ", query, ": ", errmsg); + sqlite3_free(errmsg); + } + + log_debug("List of columns in table ", table_name, ":"); + for (const auto& c: result) + log_debug(c); + return result; +} + +std::unique_ptr Sqlite3Engine::open(const std::string& filename) +{ + sqlite3* new_db; + auto res = sqlite3_open_v2(filename.data(), &new_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr); + if (res != SQLITE_OK) + { + log_error("Failed to open database file ", filename, ": ", sqlite3_errmsg(new_db)); + sqlite3_close(new_db); + throw std::runtime_error(""); + } + return std::make_unique(new_db); +} + +std::tuple Sqlite3Engine::raw_exec(const std::string& query) +{ + char* error; + const auto result = sqlite3_exec(db, query.data(), nullptr, nullptr, &error); + if (result != SQLITE_OK) + { + std::string err_msg(error); + sqlite3_free(error); + return std::make_tuple(false, err_msg); + } + return std::make_tuple(true, std::string{}); +} + +std::unique_ptr Sqlite3Engine::prepare(const std::string& query) +{ + sqlite3_stmt* stmt; + log_debug("SQLITE3: ", query); + auto res = sqlite3_prepare(db, query.data(), static_cast(query.size()) + 1, + &stmt, nullptr); + if (res != SQLITE_OK) + { + log_error("Error preparing statement: ", sqlite3_errmsg(db)); + return nullptr; + } + return std::make_unique(stmt); +} + +void Sqlite3Engine::extract_last_insert_rowid(Statement& statement) +{ + this->last_inserted_rowid = sqlite3_last_insert_rowid(this->db); + log_debug("extracted inserted ID: ", this->last_inserted_rowid); +} + +std::string Sqlite3Engine::id_column_type() +{ + return "INTEGER PRIMARY KEY AUTOINCREMENT"; +} diff --git a/src/database/sqlite3_engine.hpp b/src/database/sqlite3_engine.hpp new file mode 100644 index 0000000..eb18d0c --- /dev/null +++ b/src/database/sqlite3_engine.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include + +class Sqlite3Engine: public DatabaseEngine +{ + public: + Sqlite3Engine(sqlite3* db); + + ~Sqlite3Engine(); + + static std::unique_ptr open(const std::string& string); + + std::set get_all_columns_from_table(const std::string& table_name) override final; + std::tuple raw_exec(const std::string& query) override final; + std::unique_ptr prepare(const std::string& query) override; + void extract_last_insert_rowid(Statement& statement) override; + std::string id_column_type() override; +private: + sqlite3* const db; +}; + diff --git a/src/database/sqlite3_statement.hpp b/src/database/sqlite3_statement.hpp new file mode 100644 index 0000000..42a5220 --- /dev/null +++ b/src/database/sqlite3_statement.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include + +#include + +#include + +class Sqlite3Statement: public Statement +{ + public: + Sqlite3Statement(sqlite3_stmt* stmt): + stmt(stmt) {} + ~Sqlite3Statement() + { + sqlite3_finalize(this->stmt); + } + + StepResult step() override final + { + auto res = sqlite3_step(this->get()); + log_debug("step: ", res); + if (res == SQLITE_ROW) + return StepResult::Row; + else if (res == SQLITE_DONE) + return StepResult::Done; + else + return StepResult::Error; + } + + void bind(std::vector params) override + { + int i = 1; + for (const std::string& param: params) + { + if (sqlite3_bind_text(this->get(), i, param.data(), static_cast(param.size()), SQLITE_TRANSIENT) != SQLITE_OK) + log_error("Failed to bind ", param, " to param ", i); + i++; + } + } + + int64_t get_column_int64(const int col) override + { + return sqlite3_column_int64(this->get(), col); + } + + std::string get_column_text(const int col) override + { + const auto size = sqlite3_column_bytes(this->get(), col); + const unsigned char* str = sqlite3_column_text(this->get(), col); + std::string result(reinterpret_cast(str), static_cast(size)); + return result; + } + + bool bind_text(const int pos, const std::string& data) override + { + return sqlite3_bind_text(this->get(), pos, data.data(), static_cast(data.size()), SQLITE_TRANSIENT) == SQLITE_OK; + } + bool bind_int64(const int pos, const std::int64_t value) override + { + return sqlite3_bind_int64(this->get(), pos, static_cast(value)) == SQLITE_OK; + } + bool bind_null(const int pos) override + { + return sqlite3_bind_null(this->get(), pos) == SQLITE_OK; + } + int get_column_int(const int col) override + { + return sqlite3_column_int(this->get(), col); + } + + Sqlite3Statement(const Sqlite3Statement&) = delete; + Sqlite3Statement& operator=(const Sqlite3Statement&) = delete; + Sqlite3Statement(Sqlite3Statement&& other): + stmt(other.stmt) + { + other.stmt = nullptr; + } + Sqlite3Statement& operator=(Sqlite3Statement&& other) + { + this->stmt = other.stmt; + other.stmt = nullptr; + return *this; + } + sqlite3_stmt* get() + { + return this->stmt; + } + + private: + sqlite3_stmt* stmt; + int last_step_result{SQLITE_OK}; +}; diff --git a/src/database/statement.hpp b/src/database/statement.hpp index 87cd70f..db5f31b 100644 --- a/src/database/statement.hpp +++ b/src/database/statement.hpp @@ -1,35 +1,28 @@ #pragma once -#include +#include +#include +#include + +enum class StepResult +{ + Row, + Done, + Error, +}; class Statement { public: - Statement(sqlite3_stmt* stmt): - stmt(stmt) {} - ~Statement() - { - sqlite3_finalize(this->stmt); - } + virtual StepResult step() = 0; + + virtual void bind(std::vector params) = 0; - Statement(const Statement&) = delete; - Statement& operator=(const Statement&) = delete; - Statement(Statement&& other): - stmt(other.stmt) - { - other.stmt = nullptr; - } - Statement& operator=(Statement&& other) - { - this->stmt = other.stmt; - other.stmt = nullptr; - return *this; - } - sqlite3_stmt* get() - { - return this->stmt; - } + virtual std::int64_t get_column_int64(const int col) = 0; + virtual std::string get_column_text(const int col) = 0; + virtual int get_column_int(const int col) = 0; - private: - sqlite3_stmt* stmt; + virtual bool bind_text(const int pos, const std::string& data) = 0; + virtual bool bind_int64(const int pos, const std::int64_t value) = 0; + virtual bool bind_null(const int pos) = 0; }; diff --git a/src/database/table.cpp b/src/database/table.cpp deleted file mode 100644 index 9224d79..0000000 --- a/src/database/table.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include - -std::set get_all_columns_from_table(sqlite3* db, const std::string& table_name) -{ - std::set result; - char* errmsg; - std::string query{"PRAGMA table_info(" + table_name + ")"}; - int res = sqlite3_exec(db, query.data(), [](void* param, int columns_nb, char** columns, char**) -> int { - constexpr int name_column = 1; - std::set* result = static_cast*>(param); - if (name_column < columns_nb) - result->insert(columns[name_column]); - return 0; - }, &result, &errmsg); - - if (res != SQLITE_OK) - { - log_error("Error executing ", query, ": ", errmsg); - sqlite3_free(errmsg); - } - - return result; -} diff --git a/src/database/table.hpp b/src/database/table.hpp index 0060211..5fbc301 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -1,7 +1,8 @@ #pragma once +#include + #include -#include #include #include @@ -10,23 +11,27 @@ using namespace std::string_literals; -std::set get_all_columns_from_table(sqlite3* db, const std::string& table_name); +template +std::string ToSQLType(DatabaseEngine& db) +{ + if (std::is_same::value) + return db.id_column_type(); + else if (std::is_same::value) + return "TEXT"; + else + return "INTEGER"; +} template -void add_column_to_table(sqlite3* db, const std::string& table_name) +void add_column_to_table(DatabaseEngine& db, const std::string& table_name) { const std::string name = ColumnType::name; - std::string query{"ALTER TABLE " + table_name + " ADD " + ColumnType::name + " " + TypeToSQLType::type}; - char* error; - const auto result = sqlite3_exec(db, query.data(), nullptr, nullptr, &error); - if (result != SQLITE_OK) - { - log_error("Error adding column ", name, " to table ", table_name, ": ", error); - sqlite3_free(error); - } + std::string query{"ALTER TABLE " + table_name + " ADD " + ColumnType::name + " " + ToSQLType(db)}; + auto res = db.raw_exec(query); + if (std::get<0>(res) == false) + log_error("Error adding column ", name, " to table ", table_name, ": ", std::get<1>(res)); } - template void append_option(std::string& s) { @@ -50,27 +55,24 @@ class Table name(std::move(name)) {} - void upgrade(sqlite3* db) + void upgrade(DatabaseEngine& db) { - const auto existing_columns = get_all_columns_from_table(db, this->name); + const auto existing_columns = db.get_all_columns_from_table(this->name); add_column_if_not_exists(db, existing_columns); } - void create(sqlite3* db) + void create(DatabaseEngine& db) { - std::string res{"CREATE TABLE IF NOT EXISTS "}; - res += this->name; - res += " (\n"; - this->add_column_create(res); - res += ")"; - - char* error; - const auto result = sqlite3_exec(db, res.data(), nullptr, nullptr, &error); - if (result != SQLITE_OK) - { - log_error("Error executing query: ", error); - sqlite3_free(error); - } + std::string query{"CREATE TABLE IF NOT EXISTS "}; + query += this->name; + query += " (\n"; + this->add_column_create(db, query); + query += ")"; + + log_debug("create:" , query); + auto result = db.raw_exec(query); + if (std::get<0>(result) == false) + log_error("Error executing query: ", std::get<1>(result)); } RowType row() @@ -78,7 +80,7 @@ class Table return {this->name}; } - SelectQuery select() + auto select() { SelectQuery select(this->name); return select; @@ -93,39 +95,44 @@ class Table template typename std::enable_if::type - add_column_if_not_exists(sqlite3* db, const std::set& existing_columns) + add_column_if_not_exists(DatabaseEngine& db, const std::set& existing_columns) { using ColumnType = typename std::remove_reference(std::declval()))>::type; - if (existing_columns.count(ColumnType::name) != 1) - { - add_column_to_table(db, this->name); - } + if (existing_columns.count(ColumnType::name) == 0) + add_column_to_table(db, this->name); add_column_if_not_exists(db, existing_columns); } template typename std::enable_if::type - add_column_if_not_exists(sqlite3*, const std::set&) + add_column_if_not_exists(DatabaseEngine&, const std::set&) {} template typename std::enable_if::type - add_column_create(std::string& str) + add_column_create(DatabaseEngine& db, std::string& str) { using ColumnType = typename std::remove_reference(std::declval()))>::type; - using RealType = typename ColumnType::real_type; +// using RealType = typename ColumnType::real_type; str += ColumnType::name; str += " "; - str += TypeToSQLType::type; - append_option(str); +// if (std::is_same::value) +// { +// str += "INTEGER PRIMARY KEY AUTOINCREMENT"; +// } +// else +// { + str += ToSQLType(db); +// append_option(str); +// } if (N != sizeof...(T) - 1) str += ","; str += "\n"; - add_column_create(str); + add_column_create(db, str); } template typename std::enable_if::type - add_column_create(std::string&) + add_column_create(DatabaseEngine&, std::string&) { } const std::string name; diff --git a/src/database/type_to_sql.cpp b/src/database/type_to_sql.cpp deleted file mode 100644 index bcd9daa..0000000 --- a/src/database/type_to_sql.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -template <> const std::string TypeToSQLType::type = "INTEGER"; -template <> const std::string TypeToSQLType::type = "INTEGER"; -template <> const std::string TypeToSQLType::type = "INTEGER"; -template <> const std::string TypeToSQLType::type = "INTEGER"; -template <> const std::string TypeToSQLType::type = "INTEGER"; -template <> const std::string TypeToSQLType::type = "TEXT"; -template <> const std::string TypeToSQLType::type = "INTEGER"; \ No newline at end of file diff --git a/src/database/type_to_sql.hpp b/src/database/type_to_sql.hpp deleted file mode 100644 index ba806ab..0000000 --- a/src/database/type_to_sql.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -#include - -template -struct TypeToSQLType { static const std::string type; }; - -template <> const std::string TypeToSQLType::type; -template <> const std::string TypeToSQLType::type; -template <> const std::string TypeToSQLType::type; -template <> const std::string TypeToSQLType::type; -template <> const std::string TypeToSQLType::type; -template <> const std::string TypeToSQLType::type; -template <> const std::string TypeToSQLType::type; \ No newline at end of file diff --git a/src/database/update_query.hpp b/src/database/update_query.hpp new file mode 100644 index 0000000..32befc0 --- /dev/null +++ b/src/database/update_query.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +using namespace std::string_literals; + +template +struct Index; + +template +struct Index> +{ + static const std::size_t value = 0; +}; + +template +struct Index> +{ + static const std::size_t value = Index>::value + 1; +}; + +struct UpdateQuery: public Query +{ + template + UpdateQuery(const std::string& name, const std::tuple& columns): + Query("UPDATE ") + { + this->body += name; + this->insert_col_names_and_values(columns); + } + + template + void insert_col_names_and_values(const std::tuple& columns) + { + this->body += " SET "; + this->insert_col_name_and_value(columns); + this->body += " WHERE "s + Id::name + "=$" + std::to_string(this->current_param); + } + + template + typename std::enable_if::type + insert_col_name_and_value(const std::tuple& columns) + { + using ColumnType = std::decay_t(columns))>; + + if (!std::is_same::value) + { + this->body += ColumnType::name + "=$"s + std::to_string(this->current_param); + this->current_param++; + + if (N < (sizeof...(T) - 1)) + this->body += ", "; + } + + this->insert_col_name_and_value(columns); + } + template + typename std::enable_if::type + insert_col_name_and_value(const std::tuple&) + {} + + + template + void execute(DatabaseEngine& db, const std::tuple& columns) + { + auto statement = db.prepare(this->body); + this->bind_param(columns, *statement); + this->bind_id(columns, *statement); + + statement->step(); + } + + template + typename std::enable_if::type + bind_param(const std::tuple& columns, Statement& statement, int index=1) + { + auto&& column = std::get(columns); + using ColumnType = std::decay_t; + + if (!std::is_same::value) + actual_bind(statement, column.value, index++); + + this->bind_param(columns, statement, index); + } + + template + typename std::enable_if::type + bind_param(const std::tuple&, Statement&, int) + {} + + template + void bind_id(const std::tuple& columns, Statement& statement) + { + static constexpr auto index = Index>::value; + auto&& value = std::get(columns); + + actual_bind(statement, value.value, sizeof...(T)); + } +}; diff --git a/src/main.cpp b/src/main.cpp index 5725584..284d289 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,8 @@ #include #include +#include + #ifdef UDNS_FOUND # include #endif diff --git a/src/utils/is_one_of.hpp b/src/utils/is_one_of.hpp new file mode 100644 index 0000000..4d6770e --- /dev/null +++ b/src/utils/is_one_of.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +template +struct is_one_of_implem { + static constexpr bool value = false; +}; + +template +struct is_one_of_implem { + static constexpr bool value = + std::is_same::value || is_one_of_implem::value; +}; + +template +constexpr bool is_one_of = is_one_of_implem::value; diff --git a/src/utils/optional_bool.cpp b/src/utils/optional_bool.cpp new file mode 100644 index 0000000..56fdca2 --- /dev/null +++ b/src/utils/optional_bool.cpp @@ -0,0 +1,8 @@ +#include + + +std::ostream& operator<<(std::ostream& os, const OptionalBool& o) +{ + os << o.to_string(); + return os; +} diff --git a/src/utils/optional_bool.hpp b/src/utils/optional_bool.hpp index 59bbbab..867aca2 100644 --- a/src/utils/optional_bool.hpp +++ b/src/utils/optional_bool.hpp @@ -20,7 +20,7 @@ struct OptionalBool this->is_set = false; } - std::string to_string() + std::string to_string() const { if (this->is_set == false) return "unset"; @@ -33,3 +33,5 @@ struct OptionalBool bool is_set{false}; bool value{false}; }; + +std::ostream& operator<<(std::ostream& os, const OptionalBool& o); -- cgit v1.2.3 From 4f7ad3f9855cf62649c40d912e6c3a6a2b777913 Mon Sep 17 00:00:00 2001 From: Jonas Wielicki Date: Tue, 28 Nov 2017 18:52:56 +0100 Subject: Support for full postgresql URIs (cf. https://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNSTRING) --- src/database/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/database/database.cpp b/src/database/database.cpp index b1a5ba5..19e9988 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -37,7 +37,7 @@ void Database::open(const std::string& filename) std::unique_ptr new_db; static const auto psql_prefix = "postgresql://"s; if (filename.substr(0, psql_prefix.size()) == psql_prefix) - new_db = PostgresqlEngine::open("dbname="s + filename.substr(psql_prefix.size())); + new_db = PostgresqlEngine::open(filename); else new_db = Sqlite3Engine::open(filename); if (!new_db) -- cgit v1.2.3 From 14dcc57a19229dfaf878e07229e8f3e66857a75d Mon Sep 17 00:00:00 2001 From: Jonas Wielicki Date: Tue, 28 Nov 2017 18:53:16 +0100 Subject: Make destructor of Statement virtual I got an ASAN error otherwise (type mismatch) --- src/database/statement.hpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/database/statement.hpp b/src/database/statement.hpp index db5f31b..4a61928 100644 --- a/src/database/statement.hpp +++ b/src/database/statement.hpp @@ -14,6 +14,7 @@ enum class StepResult class Statement { public: + virtual ~Statement() = default; virtual StepResult step() = 0; virtual void bind(std::vector params) = 0; -- cgit v1.2.3 From 58f78f31e65e8628bb14722eb8b4b42a558cd0e0 Mon Sep 17 00:00:00 2001 From: Jonas Wielicki Date: Tue, 28 Nov 2017 20:38:29 +0100 Subject: Make destructor of DatabaseEngine virtual --- src/database/engine.hpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/database/engine.hpp b/src/database/engine.hpp index 2dd4c21..41dccf5 100644 --- a/src/database/engine.hpp +++ b/src/database/engine.hpp @@ -20,6 +20,7 @@ class DatabaseEngine public: DatabaseEngine() = default; + virtual ~DatabaseEngine() = default; DatabaseEngine(const DatabaseEngine&) = delete; DatabaseEngine& operator=(const DatabaseEngine&) = delete; -- cgit v1.2.3 From e1c7a6518a1aeaac1786c321aee35bd5e20acf6f Mon Sep 17 00:00:00 2001 From: Jonas Wielicki Date: Wed, 29 Nov 2017 08:03:23 +0100 Subject: Actually free the database on Database::close() .release() returns the pointer and releases ownership *without* destruction. --- src/database/database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/database/database.cpp b/src/database/database.cpp index 19e9988..f9a365d 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -242,7 +242,7 @@ std::vector Database::get_full_roster() void Database::close() { - Database::db.release(); + Database::db = nullptr; } std::string Database::gen_uuid() -- cgit v1.2.3 From 414bbca0bc2bf20c4f424c2368997a46129b32cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 1 Dec 2017 14:59:22 +0100 Subject: Handle postgresql and sqlite3 libs properly Do not fail to compile when one of them is missing but the other one is not. Raise an error when trying to open a database with the missing library. see #3237 --- src/biboumi.h.cmake | 2 ++ src/database/count_query.hpp | 2 -- src/database/database.cpp | 2 -- src/database/insert_query.hpp | 2 -- src/database/postgresql_engine.cpp | 5 +++++ src/database/postgresql_engine.hpp | 27 ++++++++++++++++++++++----- src/database/query.hpp | 2 -- src/database/row.hpp | 2 -- src/database/select_query.hpp | 4 +--- src/database/sqlite3_engine.cpp | 6 ++++++ src/database/sqlite3_engine.hpp | 19 ++++++++++++++++++- src/main.cpp | 5 ++--- 12 files changed, 56 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/biboumi.h.cmake b/src/biboumi.h.cmake index 1ad9a40..5bc1004 100644 --- a/src/biboumi.h.cmake +++ b/src/biboumi.h.cmake @@ -6,6 +6,8 @@ #cmakedefine BOTAN_FOUND #cmakedefine GCRYPT_FOUND #cmakedefine UDNS_FOUND +#cmakedefine PQ_FOUND +#cmakedefine SQLITE3_FOUND #cmakedefine SOFTWARE_VERSION "${SOFTWARE_VERSION}" #cmakedefine PROJECT_NAME "${PROJECT_NAME}" #cmakedefine HAS_GET_TIME diff --git a/src/database/count_query.hpp b/src/database/count_query.hpp index b462e0f..e8d24ef 100644 --- a/src/database/count_query.hpp +++ b/src/database/count_query.hpp @@ -6,8 +6,6 @@ #include -#include - struct CountQuery: public Query { CountQuery(std::string name): diff --git a/src/database/database.cpp b/src/database/database.cpp index f9a365d..ae5654c 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -15,8 +15,6 @@ #include -#include - std::unique_ptr Database::db; Database::MucLogLineTable Database::muc_log_lines("muclogline_"); Database::GlobalOptionsTable Database::global_options("globaloptions_"); diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 2fc0512..853b7f1 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -10,8 +10,6 @@ #include #include -#include - template typename std::enable_if::type update_autoincrement_id(std::tuple& columns, Statement& statement) diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp index 9f603ab..1c01ed5 100644 --- a/src/database/postgresql_engine.cpp +++ b/src/database/postgresql_engine.cpp @@ -1,3 +1,6 @@ +#include +#ifdef PQ_FOUND + #include #include @@ -75,3 +78,5 @@ std::string PostgresqlEngine::id_column_type() { return "SERIAL"; } + +#endif diff --git a/src/database/postgresql_engine.hpp b/src/database/postgresql_engine.hpp index e6444d4..fe4fb53 100644 --- a/src/database/postgresql_engine.hpp +++ b/src/database/postgresql_engine.hpp @@ -1,16 +1,20 @@ #pragma once -#include +#include +#include +#include +#include #include +#include -#include - -#include -#include #include #include +#ifdef PQ_FOUND + +#include + class PostgresqlEngine: public DatabaseEngine { public: @@ -29,3 +33,16 @@ class PostgresqlEngine: public DatabaseEngine private: PGconn* const conn; }; + +#else + +class PostgresqlEngine +{ +public: + static std::unique_ptr open(const std::string& string) + { + throw std::runtime_error("Cannot open postgresql database "s + string + ": biboumi is not compiled with libpq."); + } +}; + +#endif diff --git a/src/database/query.hpp b/src/database/query.hpp index 2467749..7f8aecb 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -9,8 +9,6 @@ #include #include -#include - void actual_bind(Statement& statement, const std::string& value, int index); void actual_bind(Statement& statement, const std::size_t value, int index); void actual_bind(Statement& statement, const OptionalBool& value, int index); diff --git a/src/database/row.hpp b/src/database/row.hpp index b69fac5..1b50ff9 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -8,8 +8,6 @@ #include -#include - template struct Row { diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 22b342e..837a866 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -12,12 +12,10 @@ #include #include -#include - using namespace std::string_literals; template -typename std::enable_if::value, sqlite3_int64>::type +typename std::enable_if::value, std::int64_t>::type extract_row_value(Statement& statement, const int i) { return statement.get_column_int64(i); diff --git a/src/database/sqlite3_engine.cpp b/src/database/sqlite3_engine.cpp index 7e34f89..a94a892 100644 --- a/src/database/sqlite3_engine.cpp +++ b/src/database/sqlite3_engine.cpp @@ -1,3 +1,7 @@ +#include + +#ifdef SQLITE3_FOUND + #include #include @@ -91,3 +95,5 @@ std::string Sqlite3Engine::id_column_type() { return "INTEGER PRIMARY KEY AUTOINCREMENT"; } + +#endif diff --git a/src/database/sqlite3_engine.hpp b/src/database/sqlite3_engine.hpp index eb18d0c..5b8176c 100644 --- a/src/database/sqlite3_engine.hpp +++ b/src/database/sqlite3_engine.hpp @@ -4,12 +4,17 @@ #include -#include #include #include #include #include +#include + +#ifdef SQLITE3_FOUND + +#include + class Sqlite3Engine: public DatabaseEngine { public: @@ -28,3 +33,15 @@ private: sqlite3* const db; }; +#else + +class Sqlite3Engine +{ +public: + static std::unique_ptr open(const std::string& string) + { + throw std::runtime_error("Cannot open sqlite3 database "s + string + ": biboumi is not compiled with sqlite3 lib."); + } +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index 284d289..c877e43 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,8 +6,6 @@ #include #include -#include - #ifdef UDNS_FOUND # include #endif @@ -90,7 +88,8 @@ int main(int ac, char** av) #ifdef USE_DATABASE try { open_database(); - } catch (...) { + } catch (const std::exception& e) { + log_error(e.what()); return 1; } #endif -- cgit v1.2.3 From 6dc49f32844b846bd9675ed6a9d669e266122276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 1 Dec 2017 15:06:23 +0100 Subject: Fix a few warnings --- src/database/postgresql_statement.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/database/postgresql_statement.hpp b/src/database/postgresql_statement.hpp index 5016350..6e5dec8 100644 --- a/src/database/postgresql_statement.hpp +++ b/src/database/postgresql_statement.hpp @@ -73,12 +73,12 @@ class PostgresqlStatement: public Statement this->params.push_back(data); return true; } - bool bind_int64(const int pos, const std::int64_t value) override + bool bind_int64(const int, const std::int64_t value) override { this->params.push_back(std::to_string(value)); return true; } - bool bind_null(const int pos) override + bool bind_null(const int) override { this->params.push_back("NULL"); return true; @@ -99,8 +99,9 @@ private: } log_debug("body: ", body); + const int param_size = static_cast(this->params.size()); this->result = PQexecParams(this->conn, this->body.data(), - this->params.size(), + param_size, nullptr, params.data(), nullptr, -- cgit v1.2.3 From 24dc05dd979264143223e166faa032e75f986b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 3 Dec 2017 16:28:38 +0100 Subject: Run some of the ci tests against a postgresql docker container --- src/bridge/bridge.cpp | 1 + src/xmpp/biboumi_component.cpp | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 925b226..57f0628 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -1031,6 +1031,7 @@ void Bridge::send_room_history(const std::string& hostname, std::string chan_nam (void)hostname; (void)chan_name; (void)resource; + (void)history_limit; #endif } diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 0b2bba0..51ca78d 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -1080,6 +1080,9 @@ void BiboumiComponent::on_irc_client_connected(const std::string& irc_hostname, const auto local_jid = irc_hostname + "@" + this->served_hostname; if (Database::has_roster_item(local_jid, jid)) this->send_presence_to_contact(local_jid, jid, ""); +#else + (void)irc_hostname; + (void)jid; #endif } @@ -1089,6 +1092,9 @@ void BiboumiComponent::on_irc_client_disconnected(const std::string& irc_hostnam const auto local_jid = irc_hostname + "@" + this->served_hostname; if (Database::has_roster_item(local_jid, jid)) this->send_presence_to_contact(irc_hostname + "@" + this->served_hostname, jid, "unavailable"); +#else + (void)irc_hostname; + (void)jid; #endif } -- cgit v1.2.3 From c3313d0d418cb2aaaf2226eb0a7729ef567b6afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 5 Dec 2017 01:05:45 +0100 Subject: Always free the PGresult pointer returned by PQexec Fix a somewhat big memory leak --- src/database/postgresql_engine.cpp | 6 ++++++ src/database/postgresql_statement.hpp | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp index 1c01ed5..4ee4223 100644 --- a/src/database/postgresql_engine.cpp +++ b/src/database/postgresql_engine.cpp @@ -1,6 +1,8 @@ #include #ifdef PQ_FOUND +#include + #include #include @@ -53,6 +55,10 @@ std::tuple PostgresqlEngine::raw_exec(const std::string& quer { log_debug("raw_exec:", query); PGresult* res = PQexec(this->conn, query.data()); + auto sg = utils::make_scope_guard([res](){ + PQclear(res); + }); + auto res_status = PQresultStatus(res); if (res_status != PGRES_COMMAND_OK) return std::make_tuple(false, PQresultErrorMessage(res)); diff --git a/src/database/postgresql_statement.hpp b/src/database/postgresql_statement.hpp index 6e5dec8..30c3ec2 100644 --- a/src/database/postgresql_statement.hpp +++ b/src/database/postgresql_statement.hpp @@ -14,7 +14,10 @@ class PostgresqlStatement: public Statement conn(conn) {} ~PostgresqlStatement() - {} + { + PQclear(this->result); + this->result = nullptr; + } PostgresqlStatement(const PostgresqlStatement&) = delete; PostgresqlStatement& operator=(const PostgresqlStatement&) = delete; PostgresqlStatement(PostgresqlStatement&& other) = delete; -- cgit v1.2.3 From a557718bf2f16c440313e5d5409b23dcefa45ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sat, 16 Dec 2017 15:34:54 +0100 Subject: db_name also accepts postgres:// scheme for PostgreSQL connections --- src/database/database.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/database/database.cpp b/src/database/database.cpp index ae5654c..3622963 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -34,7 +34,9 @@ void Database::open(const std::string& filename) // not, just leave things untouched std::unique_ptr new_db; static const auto psql_prefix = "postgresql://"s; - if (filename.substr(0, psql_prefix.size()) == psql_prefix) + static const auto psql_prefix2 = "postgres://"s; + if ((filename.substr(0, psql_prefix.size()) == psql_prefix) || + (filename.substr(0, psql_prefix2.size()) == psql_prefix2)) new_db = PostgresqlEngine::open(filename); else new_db = Sqlite3Engine::open(filename); -- cgit v1.2.3 From 286aa58c7b42f78eb72d4f05170a8c25edeba504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 17 Dec 2017 15:21:40 +0100 Subject: Remove a warning (unused argument) --- src/database/sqlite3_engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/database/sqlite3_engine.cpp b/src/database/sqlite3_engine.cpp index a94a892..d159540 100644 --- a/src/database/sqlite3_engine.cpp +++ b/src/database/sqlite3_engine.cpp @@ -85,7 +85,7 @@ std::unique_ptr Sqlite3Engine::prepare(const std::string& query) return std::make_unique(stmt); } -void Sqlite3Engine::extract_last_insert_rowid(Statement& statement) +void Sqlite3Engine::extract_last_insert_rowid(Statement&) { this->last_inserted_rowid = sqlite3_last_insert_rowid(this->db); log_debug("extracted inserted ID: ", this->last_inserted_rowid); -- cgit v1.2.3 From 37340e593ffb61eaccc444a1efdb3aa6f784a14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 26 Dec 2017 19:52:41 +0100 Subject: Add a node on outgoing private MUC messages See https://xmpp.org/extensions/xep-0045.html#privatemessage fix #3321 --- src/bridge/bridge.cpp | 3 ++- src/xmpp/xmpp_component.cpp | 8 +++++++- src/xmpp/xmpp_component.hpp | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 57f0628..54bee84 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -862,7 +862,8 @@ void Bridge::send_message(const Iid& iid, const std::string& nick, const std::st const auto chan_name = Iid(Jid(it->second).local, {}).get_local(); for (const auto& resource: this->resources_in_chan[ChannelKey{chan_name, iid.get_server()}]) this->xmpp.send_message(it->second, this->make_xmpp_body(body, encoding), - this->user_jid + "/" + resource, "chat", true, true); + this->user_jid + "/" + + resource, "chat", true, true, true); } else { diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 24a85d7..c44b990 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -269,7 +269,8 @@ void* XmppComponent::get_receive_buffer(const size_t size) const } 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) + const std::string& type, const bool fulljid, const bool nocopy, + const bool muc_private) { Stanza message("message"); { @@ -301,6 +302,11 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con XmlSubNode nocopy(message, "no-copy"); nocopy["xmlns"] = "urn:xmpp:hints"; } + if (muc_private) + { + XmlSubNode x(message, "x"); + x["xmlns"] = MUC_USER_NS; + } } this->send_stanza(message); } diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 22d5c48..2bbbe3b 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -112,7 +112,8 @@ public: * 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, const bool nocopy=false); + const std::string& type, const bool fulljid, const bool nocopy=false, + const bool muc_private=false); /** * Send a join from a new participant */ -- cgit v1.2.3 From 131ef9946fff0f5cfd794203e819df931b72600f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 26 Dec 2017 20:21:18 +0100 Subject: Include the nodes in the MAM iq result fix #3322 --- src/xmpp/biboumi_component.cpp | 19 ++++++++++++++++++- src/xmpp/xmpp_component.cpp | 4 +++- src/xmpp/xmpp_component.hpp | 2 +- 3 files changed, 22 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 51ca78d..a998fbe 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -725,7 +725,24 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) if (!line.col().empty()) this->send_archived_message(line, to.full(), from.full(), query_id); } - this->send_iq_result_full_jid(id, from.full(), to.full()); + { + auto fin_ptr = std::make_unique("fin"); + { + XmlNode& fin = *(fin_ptr.get()); + fin["xmlns"] = MAM_NS; + XmlSubNode set(fin, "set"); + set["xmlns"] = RSM_NS; + if (!lines.empty()) + { + XmlSubNode first(set, "first"); + first["index"] = "0"; + first.set_inner(lines[0].col()); + XmlSubNode last(set, "last"); + last.set_inner(lines[lines.size() - 1].col()); + } + } + this->send_iq_result_full_jid(id, from.full(), to.full(), std::move(fin_ptr)); + } return true; } return false; diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index c44b990..8f6826e 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -640,13 +640,15 @@ 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) +void XmppComponent::send_iq_result_full_jid(const std::string& id, const std::string& to_jid, const std::string& from_full_jid, std::unique_ptr inner) { Stanza iq("iq"); iq["from"] = from_full_jid; iq["to"] = to_jid; iq["id"] = id; iq["type"] = "result"; + if (inner) + iq.add_child(std::move(inner)); this->send_stanza(iq); } diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 2bbbe3b..3950863 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -203,7 +203,7 @@ public: */ 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); + const std::string& from_full_jid, std::unique_ptr inner=nullptr); void handle_handshake(const Stanza& stanza); void handle_error(const Stanza& stanza); -- cgit v1.2.3 From 2d9f516d1d36bbdd4b114dd3652bbeaebd2fa379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 27 Dec 2017 19:23:28 +0100 Subject: =?UTF-8?q?Don=E2=80=99t=20answer=20to=20some=20requests=20towards?= =?UTF-8?q?=20MUC=20participants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These requests are only meant to be received by the room itself. The participant must answer with not-implemented instead. fix #3323 --- src/xmpp/biboumi_component.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index a998fbe..e6eca3b 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -444,8 +444,13 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) { if (iid.type == Iid::Type::Server) adhoc_handler = &this->irc_server_adhoc_commands_handler; - else + else if (iid.type == Iid::Type::Channel && to.resource.empty()) adhoc_handler = &this->irc_channel_adhoc_commands_handler; + else + { + error_name = "feature-not-implemented"; + return; + } } // Execute the command, if any, and get a result XmlNode that we // insert in our response @@ -495,7 +500,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) stanza_error.disable(); } } - else if (iid.type == Iid::Type::Channel) + else if (iid.type == Iid::Type::Channel && to.resource.empty()) { if (node.empty()) { @@ -554,7 +559,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) this->irc_server_adhoc_commands_handler); stanza_error.disable(); } - else if (iid.type == Iid::Type::Channel) + else if (iid.type == Iid::Type::Channel && to.resource.empty()) { // Get the channel's adhoc commands this->send_adhoc_commands_list(id, from, to_str, (Config::get("admin", "") == @@ -562,6 +567,8 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) this->irc_channel_adhoc_commands_handler); stanza_error.disable(); } + else // “to” is a MUC user, not the room itself + error_name = "feature-not-implemented"; } else if (node.empty() && iid.type == Iid::Type::Server) { // Disco on an IRC server: get the list of channels @@ -784,7 +791,7 @@ bool BiboumiComponent::handle_room_configuration_form_request(const std::string& { Iid iid(to.local, {'#', '&'}); - if (iid.type != Iid::Type::Channel) + if (iid.type != Iid::Type::Channel || !to.resource.empty()) return false; Stanza iq("iq"); @@ -806,7 +813,7 @@ bool BiboumiComponent::handle_room_configuration_form(const XmlNode& query, cons { Iid iid(to.local, {'#', '&'}); - if (iid.type != Iid::Type::Channel) + if (iid.type != Iid::Type::Channel || !to.resource.empty()) return false; Jid requester(from); -- cgit v1.2.3 From 8409e50c1a28cc87799a8d9c67a90abf250ff6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 27 Dec 2017 19:36:36 +0100 Subject: Fix a subtle iid parsing error in the adhoc code --- src/xmpp/biboumi_component.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index e6eca3b..8775869 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -436,7 +436,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) // Depending on the 'to' jid in the request, we use one adhoc // command handler or an other - Iid iid(to.local, {}); + Iid iid(to.local, {'#', '&'}); AdhocCommandsHandler* adhoc_handler; if (to.local.empty()) adhoc_handler = &this->adhoc_commands_handler; -- cgit v1.2.3 From 435a63a070e62a11b0a6f5a3c06c445e1a79b9f5 Mon Sep 17 00:00:00 2001 From: Ailin Nemui Date: Wed, 10 Jan 2018 13:36:53 +0100 Subject: Change max line length to more conservative constants --- src/irc/irc_client.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 5f26bf0..40078d9 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -483,12 +483,16 @@ bool IrcClient::send_channel_message(const std::string& chan_name, const std::st } // The max size is 512, taking into account the whole message, not just // the text we send. - // This includes our own nick, username and host (because this will be - // added by the server into our message), in addition to the basic - // components of the message we send (command name, chan name, \r\n et) + // This includes our own nick, constants for username and host (because these + // are notoriously hard to know what the server will use), in addition to the basic + // components of the message we send (command name, chan name, \r\n etc.) // : + NICK + ! + USER + @ + HOST + + PRIVMSG + + CHAN + + : + \r\n + // 63 is the maximum hostname length defined by the protocol. 10 seems to be + // the username limit. + constexpr auto max_username_size = 10; + constexpr auto max_hostname_size = 63; const auto line_size = 512 - - this->current_nick.size() - this->username.size() - this->own_host.size() - + this->current_nick.size() - max_username_size - max_hostname_size - ::strlen(":!@ PRIVMSG ") - chan_name.length() - ::strlen(" :\r\n"); const auto lines = cut(body, line_size); for (const auto& line: lines) -- cgit v1.2.3 From 7c8a7176d196d4bb3724cfafd41980c16be5f404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 12 Jan 2018 03:59:46 +0100 Subject: Only use sd_journal_* if we really are outputing to journald We check that the device and inode numbers are actually the same as the JOURNAL_STREAM value, instead of just checking that the value exists. This fixes the logger unit tests --- src/logger/logger.cpp | 21 ++++++++++++++++++++- src/logger/logger.hpp | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/logger/logger.cpp b/src/logger/logger.cpp index 4287794..482cb18 100644 --- a/src/logger/logger.cpp +++ b/src/logger/logger.cpp @@ -1,6 +1,10 @@ #include #include +#include +#include +#include + Logger::Logger(const int log_level): log_level(log_level), stream(std::cout.rdbuf()), @@ -8,7 +12,22 @@ Logger::Logger(const int log_level): null_stream{&null_buffer} { #ifdef SYSTEMD_FOUND - if (::getenv("JOURNAL_STREAM") != nullptr && this->use_stdout()) + if (!this->use_stdout()) + return; + + // See https://www.freedesktop.org/software/systemd/man/systemd.exec.html#%24JOURNAL_STREAM + const char* journal_stream = ::getenv("JOURNAL_STREAM"); + if (journal_stream == nullptr) + return; + + struct stat s{}; + const int res = ::fstat(STDOUT_FILENO, &s); + if (res == -1) + return; + + const auto stdout_stream = std::to_string(s.st_dev) + ":" + std::to_string(s.st_ino); + + if (stdout_stream == journal_stream) this->use_systemd = true; #endif } diff --git a/src/logger/logger.hpp b/src/logger/logger.hpp index a99648c..1689866 100644 --- a/src/logger/logger.hpp +++ b/src/logger/logger.hpp @@ -9,6 +9,7 @@ */ #include +#include #include #include #include -- cgit v1.2.3 From b4b9828de50c09fea5503b3b3ead2ae9d5144404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Fri, 12 Jan 2018 19:54:14 +0100 Subject: Follow log_level even when using journald output fix #3328 --- src/logger/logger.hpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/logger/logger.hpp b/src/logger/logger.hpp index 1689866..315fc11 100644 --- a/src/logger/logger.hpp +++ b/src/logger/logger.hpp @@ -74,8 +74,8 @@ public: bool use_systemd{false}; #endif -private: const int log_level; +private: std::ofstream ofstream{}; std::ostream stream; @@ -105,13 +105,16 @@ namespace logging_details if (Logger::instance()->use_systemd) { (void)level; - std::ostringstream os; - log(os, std::forward(args)...); - sd_journal_send("MESSAGE=%s", os.str().data(), - "PRIORITY=%i", syslog_level, - "CODE_FILE=%s", src_file, - "CODE_LINE=%i", line, - nullptr); + if (level >= Logger::instance()->log_level) + { + std::ostringstream os; + log(os, std::forward(args)...); + sd_journal_send("MESSAGE=%s", os.str().data(), + "PRIORITY=%i", syslog_level, + "CODE_FILE=%s", src_file, + "CODE_LINE=%i", line, + nullptr); + } } else { -- cgit v1.2.3 From 3cd649023680bd49f9865fd62474d4db6a9d7c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 14 Jan 2018 21:44:22 +0100 Subject: Remove all the ugly database debug --- src/database/insert_query.hpp | 5 +---- src/database/postgresql_engine.cpp | 3 --- src/database/postgresql_statement.hpp | 13 ++----------- src/database/query.cpp | 3 --- src/database/select_query.hpp | 1 - src/database/sqlite3_engine.cpp | 5 ----- src/database/sqlite3_statement.hpp | 1 - src/database/table.hpp | 15 ++------------- 8 files changed, 5 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 853b7f1..17cc245 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -16,10 +16,7 @@ update_autoincrement_id(std::tuple& columns, Statement& statement) { using ColumnType = typename std::decay(columns))>::type; if (std::is_same::value) - { - log_debug("EXTRACTING LAST ID"); - auto&& column = std::get(columns); - } + auto&& column = std::get(columns); update_autoincrement_id(columns, statement); } diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp index 4ee4223..150ca42 100644 --- a/src/database/postgresql_engine.cpp +++ b/src/database/postgresql_engine.cpp @@ -20,7 +20,6 @@ PostgresqlEngine::~PostgresqlEngine() std::unique_ptr PostgresqlEngine::open(const std::string& conninfo) { - log_debug("trying to open: ", conninfo); PGconn* con = PQconnectdb(conninfo.data()); if (!con) @@ -47,13 +46,11 @@ std::set PostgresqlEngine::get_all_columns_from_table(const std::st while (statement->step() == StepResult::Row) columns.insert(statement->get_column_text(0)); - log_debug("found ", columns.size(), " columns."); return columns; } std::tuple PostgresqlEngine::raw_exec(const std::string& query) { - log_debug("raw_exec:", query); PGresult* res = PQexec(this->conn, query.data()); auto sg = utils::make_scope_guard([res](){ PQclear(res); diff --git a/src/database/postgresql_statement.hpp b/src/database/postgresql_statement.hpp index 30c3ec2..571c8f1 100644 --- a/src/database/postgresql_statement.hpp +++ b/src/database/postgresql_statement.hpp @@ -96,12 +96,7 @@ private: params.reserve(this->params.size()); for (const auto& param: this->params) - { - log_debug("param:", param); - params.push_back(param.data()); - } - - log_debug("body: ", body); + params.push_back(param.data()); const int param_size = static_cast(this->params.size()); this->result = PQexecParams(this->conn, this->body.data(), param_size, @@ -111,11 +106,7 @@ private: nullptr, 0); const auto status = PQresultStatus(this->result); - if (status == PGRES_TUPLES_OK) - { - log_debug("PGRES_TUPLES_OK"); - } - else if (status != PGRES_COMMAND_OK) + if (status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK) { log_error("Failed to execute command: ", PQresultErrorMessage(this->result)); return false; diff --git a/src/database/query.cpp b/src/database/query.cpp index 6f305b2..4054007 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -3,19 +3,16 @@ void actual_bind(Statement& statement, const std::string& value, int index) { - log_debug("binding string:", value, " to col ", index); statement.bind_text(index, value); } void actual_bind(Statement& statement, const std::size_t value, int index) { - log_debug("binding size_t:", value); statement.bind_int64(index, value); } void actual_bind(Statement& statement, const OptionalBool& value, int index) { - log_debug("binding optional_t:", value.to_string()); if (!value.is_set) statement.bind_int64(index, 0); else if (value.value) diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index 837a866..af2ac1c 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -115,7 +115,6 @@ struct SelectQuery: public Query while (statement->step() == StepResult::Row) { - log_debug("one result."); Row row(this->table_name); extract_row_values(row, *statement); rows.push_back(row); diff --git a/src/database/sqlite3_engine.cpp b/src/database/sqlite3_engine.cpp index d159540..92d514b 100644 --- a/src/database/sqlite3_engine.cpp +++ b/src/database/sqlite3_engine.cpp @@ -39,9 +39,6 @@ std::set Sqlite3Engine::get_all_columns_from_table(const std::strin sqlite3_free(errmsg); } - log_debug("List of columns in table ", table_name, ":"); - for (const auto& c: result) - log_debug(c); return result; } @@ -74,7 +71,6 @@ std::tuple Sqlite3Engine::raw_exec(const std::string& query) std::unique_ptr Sqlite3Engine::prepare(const std::string& query) { sqlite3_stmt* stmt; - log_debug("SQLITE3: ", query); auto res = sqlite3_prepare(db, query.data(), static_cast(query.size()) + 1, &stmt, nullptr); if (res != SQLITE_OK) @@ -88,7 +84,6 @@ std::unique_ptr Sqlite3Engine::prepare(const std::string& query) void Sqlite3Engine::extract_last_insert_rowid(Statement&) { this->last_inserted_rowid = sqlite3_last_insert_rowid(this->db); - log_debug("extracted inserted ID: ", this->last_inserted_rowid); } std::string Sqlite3Engine::id_column_type() diff --git a/src/database/sqlite3_statement.hpp b/src/database/sqlite3_statement.hpp index 42a5220..7738fa6 100644 --- a/src/database/sqlite3_statement.hpp +++ b/src/database/sqlite3_statement.hpp @@ -19,7 +19,6 @@ class Sqlite3Statement: public Statement StepResult step() override final { auto res = sqlite3_step(this->get()); - log_debug("step: ", res); if (res == SQLITE_ROW) return StepResult::Row; else if (res == SQLITE_DONE) diff --git a/src/database/table.hpp b/src/database/table.hpp index 5fbc301..680e7cc 100644 --- a/src/database/table.hpp +++ b/src/database/table.hpp @@ -65,11 +65,10 @@ class Table { std::string query{"CREATE TABLE IF NOT EXISTS "}; query += this->name; - query += " (\n"; + query += " ("; this->add_column_create(db, query); query += ")"; - log_debug("create:" , query); auto result = db.raw_exec(query); if (std::get<0>(result) == false) log_error("Error executing query: ", std::get<1>(result)); @@ -112,21 +111,11 @@ class Table add_column_create(DatabaseEngine& db, std::string& str) { using ColumnType = typename std::remove_reference(std::declval()))>::type; -// using RealType = typename ColumnType::real_type; str += ColumnType::name; str += " "; -// if (std::is_same::value) -// { -// str += "INTEGER PRIMARY KEY AUTOINCREMENT"; -// } -// else -// { - str += ToSQLType(db); -// append_option(str); -// } + str += ToSQLType(db); if (N != sizeof...(T) - 1) str += ","; - str += "\n"; add_column_create(db, str); } -- cgit v1.2.3 From 7e64a2e361adcdbd2fce5ad76051a150b4de062d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 14 Jan 2018 21:46:49 +0100 Subject: Add a DEBUG_SQL_QUERIES to log info about the executed SQL queries fix #3324 --- src/biboumi.h.cmake | 2 ++ src/database/count_query.hpp | 3 +++ src/database/insert_query.hpp | 4 ++++ src/database/postgresql_engine.cpp | 6 ++++++ src/database/query.hpp | 29 +++++++++++++++++++++++++++++ src/database/select_query.hpp | 4 ++++ src/database/sqlite3_engine.cpp | 7 +++++++ src/database/update_query.hpp | 4 ++++ src/utils/scopetimer.hpp | 17 +++++++++++++++++ 9 files changed, 76 insertions(+) create mode 100644 src/utils/scopetimer.hpp (limited to 'src') diff --git a/src/biboumi.h.cmake b/src/biboumi.h.cmake index 5bc1004..fa99cd4 100644 --- a/src/biboumi.h.cmake +++ b/src/biboumi.h.cmake @@ -12,3 +12,5 @@ #cmakedefine PROJECT_NAME "${PROJECT_NAME}" #cmakedefine HAS_GET_TIME #cmakedefine HAS_PUT_TIME +#cmakedefine DEBUG_SQL_QUERIES + diff --git a/src/database/count_query.hpp b/src/database/count_query.hpp index e8d24ef..118ce44 100644 --- a/src/database/count_query.hpp +++ b/src/database/count_query.hpp @@ -16,6 +16,9 @@ struct CountQuery: public Query int64_t execute(DatabaseEngine& db) { +#ifdef DEBUG_SQL_QUERIES + const auto timer = this->log_and_time(); +#endif auto statement = db.prepare(this->body); int64_t res = 0; if (statement->step() != StepResult::Error) diff --git a/src/database/insert_query.hpp b/src/database/insert_query.hpp index 17cc245..9726424 100644 --- a/src/database/insert_query.hpp +++ b/src/database/insert_query.hpp @@ -39,6 +39,10 @@ struct InsertQuery: public Query template void execute(DatabaseEngine& db, std::tuple& columns) { +#ifdef DEBUG_SQL_QUERIES + const auto timer = this->log_and_time(); +#endif + auto statement = db.prepare(this->body); this->bind_param(columns, *statement); diff --git a/src/database/postgresql_engine.cpp b/src/database/postgresql_engine.cpp index 150ca42..984a959 100644 --- a/src/database/postgresql_engine.cpp +++ b/src/database/postgresql_engine.cpp @@ -3,6 +3,8 @@ #include +#include + #include #include @@ -51,6 +53,10 @@ std::set PostgresqlEngine::get_all_columns_from_table(const std::st std::tuple PostgresqlEngine::raw_exec(const std::string& query) { +#ifdef DEBUG_SQL_QUERIES + log_debug("SQL QUERY: ", query); + const auto timer = make_sql_timer(); +#endif PGresult* res = PQexec(this->conn, query.data()); auto sg = utils::make_scope_guard([res](){ PQclear(res); diff --git a/src/database/query.hpp b/src/database/query.hpp index 7f8aecb..547138f 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -13,6 +15,20 @@ void actual_bind(Statement& statement, const std::string& value, int index); void actual_bind(Statement& statement, const std::size_t value, int index); void actual_bind(Statement& statement, const OptionalBool& value, int index); +#ifdef DEBUG_SQL_QUERIES +#include + +inline auto make_sql_timer() +{ + return make_scope_timer([](const std::chrono::steady_clock::duration& elapsed) + { + const auto seconds = std::chrono::duration_cast(elapsed); + const auto rest = elapsed - seconds; + log_debug("Query executed in ", seconds.count(), ".", rest.count(), "s."); + }); +} +#endif + struct Query { std::string body; @@ -22,6 +38,18 @@ struct Query Query(std::string str): body(std::move(str)) {} + +#ifdef DEBUG_SQL_QUERIES + auto log_and_time() + { + std::ostringstream os; + os << this->body << "; "; + for (const auto& param: this->params) + os << "'" << param << "' "; + log_debug("SQL QUERY: ", os.str()); + return make_sql_timer(); + } +#endif }; template @@ -58,3 +86,4 @@ operator<<(Query& query, const Integer& i) actual_add_param(query, i); return query; } + diff --git a/src/database/select_query.hpp b/src/database/select_query.hpp index af2ac1c..5a17f38 100644 --- a/src/database/select_query.hpp +++ b/src/database/select_query.hpp @@ -110,6 +110,10 @@ struct SelectQuery: public Query { std::vector> rows; +#ifdef DEBUG_SQL_QUERIES + const auto timer = this->log_and_time(); +#endif + auto statement = db.prepare(this->body); statement->bind(std::move(this->params)); diff --git a/src/database/sqlite3_engine.cpp b/src/database/sqlite3_engine.cpp index 92d514b..ae4a146 100644 --- a/src/database/sqlite3_engine.cpp +++ b/src/database/sqlite3_engine.cpp @@ -6,6 +6,8 @@ #include +#include + #include #include #include @@ -57,6 +59,11 @@ std::unique_ptr Sqlite3Engine::open(const std::string& filename) std::tuple Sqlite3Engine::raw_exec(const std::string& query) { +#ifdef DEBUG_SQL_QUERIES + log_debug("SQL QUERY: ", query); + const auto timer = make_sql_timer(); +#endif + char* error; const auto result = sqlite3_exec(db, query.data(), nullptr, nullptr, &error); if (result != SQLITE_OK) diff --git a/src/database/update_query.hpp b/src/database/update_query.hpp index 32befc0..a29ac3f 100644 --- a/src/database/update_query.hpp +++ b/src/database/update_query.hpp @@ -64,6 +64,10 @@ struct UpdateQuery: public Query template void execute(DatabaseEngine& db, const std::tuple& columns) { +#ifdef DEBUG_SQL_QUERIES + const auto timer = this->log_and_time(); +#endif + auto statement = db.prepare(this->body); this->bind_param(columns, *statement); this->bind_id(columns, *statement); diff --git a/src/utils/scopetimer.hpp b/src/utils/scopetimer.hpp new file mode 100644 index 0000000..7d3db9b --- /dev/null +++ b/src/utils/scopetimer.hpp @@ -0,0 +1,17 @@ +#include + +#include + +#include + +template +auto make_scope_timer(Callback cb) +{ + const auto start_time = std::chrono::steady_clock::now(); + return utils::make_scope_guard([start_time, cb = std::move(cb)]() + { + const auto now = std::chrono::steady_clock::now(); + const auto elapsed = now - start_time; + cb(elapsed); + }); +} -- cgit v1.2.3 From 520711023f82bc0828a7af9459eabb155bf42566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 17 Jan 2018 20:59:38 +0100 Subject: Use our botan gh#1276 workaround only for botan < 2.4 fix #3320 --- src/network/tcp_socket_handler.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp index 602cf94..642cf03 100644 --- a/src/network/tcp_socket_handler.cpp +++ b/src/network/tcp_socket_handler.cpp @@ -12,6 +12,7 @@ #include #ifdef BOTAN_FOUND +# include # include # include # include @@ -28,8 +29,10 @@ namespace Botan::TLS::Session_Manager_In_Memory& get_session_manager() { static Botan::TLS::Session_Manager_In_Memory session_manager{get_rng()}; +#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(2,4,0) // workaround for https://github.com/randombit/botan/issues/1276 session_manager.remove_all(); +#endif return session_manager; } } -- cgit v1.2.3 From f371d9ca46578a722d2ce0d4a88ea35f64dd1d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 18 Jan 2018 19:34:56 +0100 Subject: xep-0106 escape the JIDs listed in a disco#items server query fix #3325 --- src/xmpp/biboumi_component.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 8775869..a0e52e6 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -1010,7 +1011,9 @@ void BiboumiComponent::send_iq_room_list_result(const std::string& id, const std for (auto it = begin; it != end; ++it) { XmlSubNode item(query, "item"); - item["jid"] = it->channel + "@" + this->served_hostname; + std::string channel_name = it->channel; + xep0106::encode(channel_name); + item["jid"] = channel_name + "@" + this->served_hostname; } if ((rs_info.max >= 0 || !rs_info.after.empty() || !rs_info.before.empty())) -- cgit v1.2.3 From 06271729e33300cebbd7222f50a2b38905e33cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 22 Jan 2018 20:57:34 +0100 Subject: Fix a crash happening when a user cancels a non-existing ad-hoc session --- src/xmpp/adhoc_commands_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/xmpp/adhoc_commands_handler.cpp b/src/xmpp/adhoc_commands_handler.cpp index e4dcd5c..bb48781 100644 --- a/src/xmpp/adhoc_commands_handler.cpp +++ b/src/xmpp/adhoc_commands_handler.cpp @@ -83,7 +83,7 @@ XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, co XmlSubNode next(actions, "next"); } } - else if (action == "cancel") + else if (session_it != this->sessions.end() && action == "cancel") { this->sessions.erase(session_it); command_node["status"] = "canceled"; -- cgit v1.2.3 From 33a5f1355d1250bf77184459a8d40a790e42814d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 24 Jan 2018 21:42:26 +0100 Subject: Remove a variable template usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because it’s only supported in gcc>=5.0 --- src/database/row.hpp | 4 ++-- src/utils/is_one_of.hpp | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/database/row.hpp b/src/database/row.hpp index 1b50ff9..2d55897 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -30,13 +30,13 @@ struct Row } template - void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) + void save(std::unique_ptr& db, typename std::enable_if::value && Coucou>::type* = nullptr) { this->insert(*db); } template - void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) + void save(std::unique_ptr& db, typename std::enable_if::value && Coucou>::type* = nullptr) { const Id& id = std::get(this->columns); if (id.value == Id::unset_value) diff --git a/src/utils/is_one_of.hpp b/src/utils/is_one_of.hpp index 4d6770e..c706421 100644 --- a/src/utils/is_one_of.hpp +++ b/src/utils/is_one_of.hpp @@ -3,15 +3,12 @@ #include template -struct is_one_of_implem { +struct is_one_of { static constexpr bool value = false; }; template -struct is_one_of_implem { +struct is_one_of { static constexpr bool value = - std::is_same::value || is_one_of_implem::value; + std::is_same::value || is_one_of::value; }; - -template -constexpr bool is_one_of = is_one_of_implem::value; -- cgit v1.2.3 From e267512ad40c073bd5a5b37a4ee3378c80b9f523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Thu, 25 Jan 2018 02:17:20 +0100 Subject: Restore the is_one_of variable template --- src/database/row.hpp | 4 ++-- src/utils/is_one_of.hpp | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/database/row.hpp b/src/database/row.hpp index 2d55897..130863a 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -30,13 +30,13 @@ struct Row } template - void save(std::unique_ptr& db, typename std::enable_if::value && Coucou>::type* = nullptr) + void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) { this->insert(*db); } template - void save(std::unique_ptr& db, typename std::enable_if::value && Coucou>::type* = nullptr) + void save(std::unique_ptr& db, typename std::enable_if && Coucou>::type* = nullptr) { const Id& id = std::get(this->columns); if (id.value == Id::unset_value) diff --git a/src/utils/is_one_of.hpp b/src/utils/is_one_of.hpp index c706421..4d6770e 100644 --- a/src/utils/is_one_of.hpp +++ b/src/utils/is_one_of.hpp @@ -3,12 +3,15 @@ #include template -struct is_one_of { +struct is_one_of_implem { static constexpr bool value = false; }; template -struct is_one_of { +struct is_one_of_implem { static constexpr bool value = - std::is_same::value || is_one_of::value; + std::is_same::value || is_one_of_implem::value; }; + +template +constexpr bool is_one_of = is_one_of_implem::value; -- cgit v1.2.3 From 9bf81a2c04eddabd0f09ea9157e6e7c97bea88f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 28 Jan 2018 14:10:14 +0100 Subject: This should fix the int conversion warning on 32bits arch --- src/database/query.cpp | 2 +- src/database/query.hpp | 2 +- src/database/row.hpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/database/query.cpp b/src/database/query.cpp index 4054007..d27dc59 100644 --- a/src/database/query.cpp +++ b/src/database/query.cpp @@ -6,7 +6,7 @@ void actual_bind(Statement& statement, const std::string& value, int index) statement.bind_text(index, value); } -void actual_bind(Statement& statement, const std::size_t value, int index) +void actual_bind(Statement& statement, const std::int64_t value, int index) { statement.bind_int64(index, value); } diff --git a/src/database/query.hpp b/src/database/query.hpp index 547138f..8434944 100644 --- a/src/database/query.hpp +++ b/src/database/query.hpp @@ -12,7 +12,7 @@ #include void actual_bind(Statement& statement, const std::string& value, int index); -void actual_bind(Statement& statement, const std::size_t value, int index); +void actual_bind(Statement& statement, const std::int64_t value, int index); void actual_bind(Statement& statement, const OptionalBool& value, int index); #ifdef DEBUG_SQL_QUERIES diff --git a/src/database/row.hpp b/src/database/row.hpp index 130863a..4dc98be 100644 --- a/src/database/row.hpp +++ b/src/database/row.hpp @@ -42,7 +42,8 @@ struct Row if (id.value == Id::unset_value) { this->insert(*db); - std::get(this->columns).value = db->last_inserted_rowid; + if (db->last_inserted_rowid >= 0) + std::get(this->columns).value = static_cast(db->last_inserted_rowid); } else this->update(*db); -- cgit v1.2.3 From 442f46c4acf728e7a189327598a0ea3d33010d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 28 Jan 2018 14:15:34 +0100 Subject: And an other conversion warning --- src/xmpp/xmpp_component.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 8f6826e..053fc3e 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -22,6 +22,7 @@ #include #ifdef SYSTEMD_FOUND # include +#include #endif using namespace std::string_literals; @@ -398,7 +399,7 @@ 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) +void XmppComponent::send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body_txt, const std::string& jid_to, Database::time_point::rep timestamp) { Stanza message("message"); message["to"] = jid_to; -- cgit v1.2.3 From 7403c397edb14ba93d014ab10e13a97c817ff0b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 28 Jan 2018 14:20:21 +0100 Subject: Correctly include the database header --- src/xmpp/xmpp_component.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 053fc3e..88baf6e 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -22,7 +23,6 @@ #include #ifdef SYSTEMD_FOUND # include -#include #endif using namespace std::string_literals; -- cgit v1.2.3 From 17f2cb5c93e2c86f68cda97fed43fb617bd75ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Mon, 29 Jan 2018 21:11:20 +0100 Subject: =?UTF-8?q?Do=20not=20forget=20the=20complete=3D'true'=20attribute?= =?UTF-8?q?=20in=20MAM=E2=80=99s=20result=20iq?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/xmpp/biboumi_component.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index a0e52e6..ee8a502 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -738,6 +738,7 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) { XmlNode& fin = *(fin_ptr.get()); fin["xmlns"] = MAM_NS; + fin["complete"] = "true"; XmlSubNode set(fin, "set"); set["xmlns"] = RSM_NS; if (!lines.empty()) -- cgit v1.2.3 From cb831788942b49a28bd79fd62dbdc3d00f15b227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 30 Jan 2018 09:07:21 +0100 Subject: Add the complete='true' attribute only when appropriate --- src/xmpp/biboumi_component.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index ee8a502..dd66aca 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -721,13 +721,25 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) if (max) limit = std::atoi(max->get_inner().data()); } - // If the archive is really big, and the client didn’t specify any - // limit, we avoid flooding it: we set an arbitrary max limit. - if (limit == -1 && start.empty() && end.empty()) + // Do send more than 100 messages, even if the client asked for more, + // or if it didn’t specify any limit. + // 101 is just a trick to know if there are more available messages. + // If our query returns 101 message, we know it’s incomplete, but we + // still send only 100 + if ((limit == -1 && start.empty() && end.empty()) + || limit > 100) + limit = 101; + log_debug("limit: ", limit); + auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), limit, start, end); + bool complete = true; + if (lines.size() > 100) { - limit = 100; + log_debug("incomplete"); + complete = false; + log_debug("size of lines before erase: ", lines.size()); + lines.erase(lines.begin(), std::prev(lines.end(), 100)); + log_debug("size of lines after erase: ", lines.size()); } - const auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), limit, start, end); for (const Database::MucLogLine& line: lines) { if (!line.col().empty()) @@ -738,7 +750,8 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) { XmlNode& fin = *(fin_ptr.get()); fin["xmlns"] = MAM_NS; - fin["complete"] = "true"; + if (complete) + fin["complete"] = "true"; XmlSubNode set(fin, "set"); set["xmlns"] = RSM_NS; if (!lines.empty()) -- cgit v1.2.3 From 58fd68916636df2372f8187b375245ef5922833a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 30 Jan 2018 09:14:26 +0100 Subject: Add a ifndef USE_DATABASE guard around send_history_message --- src/xmpp/xmpp_component.cpp | 3 ++- src/xmpp/xmpp_component.hpp | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 88baf6e..9be9e34 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -399,6 +398,7 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str this->send_stanza(message); } +#ifdef USE_DATABASE void XmppComponent::send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body_txt, const std::string& jid_to, Database::time_point::rep timestamp) { Stanza message("message"); @@ -422,6 +422,7 @@ void XmppComponent::send_history_message(const std::string& muc_name, const std: this->send_stanza(message); } +#endif void XmppComponent::send_muc_leave(const std::string& muc_name, const std::string& nick, Xmpp::body&& message, const std::string& jid_to, const bool self, const bool user_requested) diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 3950863..cef26c1 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -1,8 +1,10 @@ #pragma once +#include "biboumi.h" #include #include +#include #include #include @@ -133,11 +135,13 @@ public: */ void send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& body, const std::string& jid_to, std::string uuid); +#ifdef USE_DATABASE /** * Send a message, with a 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); +#endif /** * Send an unavailable presence for this nick */ -- cgit v1.2.3 From 369da19d41142a20235372f3bfbe180e41008b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Tue, 30 Jan 2018 09:16:12 +0100 Subject: Remove the debug logs from previous commit --- src/xmpp/biboumi_component.cpp | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index dd66aca..481ebb9 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -729,16 +729,12 @@ bool BiboumiComponent::handle_mam_request(const Stanza& stanza) if ((limit == -1 && start.empty() && end.empty()) || limit > 100) limit = 101; - log_debug("limit: ", limit); auto lines = Database::get_muc_logs(from.bare(), iid.get_local(), iid.get_server(), limit, start, end); bool complete = true; if (lines.size() > 100) { - log_debug("incomplete"); complete = false; - log_debug("size of lines before erase: ", lines.size()); lines.erase(lines.begin(), std::prev(lines.end(), 100)); - log_debug("size of lines after erase: ", lines.size()); } for (const Database::MucLogLine& line: lines) { -- cgit v1.2.3 From d7cf736adb4837c55d8112160cd4718e549ebaaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 4 Feb 2018 11:02:56 +0100 Subject: Fix argument types in declaration of send_history_message --- src/xmpp/xmpp_component.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index cef26c1..1daa6fb 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -140,7 +140,7 @@ public: * Send a message, with a 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); + const std::string& jid_to, Database::time_point::rep timestamp); #endif /** * Send an unavailable presence for this nick -- cgit v1.2.3 From 1ebd8a2321c454129d921dc71777f47b97b8db97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Sun, 4 Feb 2018 16:58:04 +0100 Subject: Fix conversion warnings on 32 bits --- src/utils/time.cpp | 3 ++- src/utils/time.hpp | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/utils/time.cpp b/src/utils/time.cpp index bc2c18d..71306fd 100644 --- a/src/utils/time.cpp +++ b/src/utils/time.cpp @@ -9,9 +9,10 @@ namespace utils { -std::string to_string(const std::time_t& timestamp) +std::string to_string(const std::chrono::system_clock::time_point::rep& time) { constexpr std::size_t stamp_size = 21; + const std::time_t timestamp = static_cast(time); char date_buf[stamp_size]; if (std::strftime(date_buf, stamp_size, "%FT%TZ", std::gmtime(×tamp)) != stamp_size - 1) return ""; diff --git a/src/utils/time.hpp b/src/utils/time.hpp index c71cd9c..4b19634 100644 --- a/src/utils/time.hpp +++ b/src/utils/time.hpp @@ -2,9 +2,10 @@ #include #include +#include namespace utils { -std::string to_string(const std::time_t& timestamp); +std::string to_string(const std::chrono::system_clock::time_point::rep& timestamp); std::time_t parse_datetime(const std::string& stamp); -} \ No newline at end of file +} -- cgit v1.2.3