#include "biboumi.h" #ifdef USE_DATABASE #include #include #include #include #include #include #include #include #include #include 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{}; Database::GlobalPersistent::GlobalPersistent(): Column{Config::get_bool("persistent_by_default", false)} {} 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 std::unique_ptr new_db; static const auto psql_prefix = "postgresql://"s; 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); 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()); } Database::GlobalOptions Database::get_global_options(const std::string& owner) { auto request = Database::global_options.select(); request.where() << Owner{} << "=" << owner; Database::GlobalOptions options{Database::global_options.get_name()}; auto result = request.execute(*Database::db); if (result.size() == 1) options = result.front(); else options.col() = owner; return options; } Database::IrcServerOptions Database::get_irc_server_options(const std::string& owner, const std::string& server) { auto request = Database::irc_server_options.select(); request.where() << Owner{} << "=" << owner << " and " << Server{} << "=" << server; Database::IrcServerOptions options{Database::irc_server_options.get_name()}; auto result = request.execute(*Database::db); if (result.size() == 1) options = result.front(); else { options.col() = owner; options.col() = server; } return options; } Database::IrcChannelOptions Database::get_irc_channel_options(const std::string& owner, const std::string& server, const std::string& channel) { auto request = Database::irc_channel_options.select(); request.where() << Owner{} << "=" << owner <<\ " and " << Server{} << "=" << server <<\ " and " << Channel{} << "=" << channel; Database::IrcChannelOptions options{Database::irc_channel_options.get_name()}; auto result = request.execute(*Database::db); if (result.size() == 1) options = result.front(); else { options.col() = owner; options.col() = server; options.col() = channel; } return options; } Database::IrcChannelOptions Database::get_irc_channel_options_with_server_default(const std::string& owner, const std::string& server, const std::string& channel) { auto coptions = Database::get_irc_channel_options(owner, server, channel); auto soptions = Database::get_irc_server_options(owner, server); coptions.col() = get_first_non_empty(coptions.col(), soptions.col()); coptions.col() = get_first_non_empty(coptions.col(), soptions.col()); coptions.col() = get_first_non_empty(coptions.col(), soptions.col()); return coptions; } Database::IrcChannelOptions Database::get_irc_channel_options_with_server_and_global_default(const std::string& owner, const std::string& server, const std::string& channel) { auto coptions = Database::get_irc_channel_options(owner, server, channel); auto soptions = Database::get_irc_server_options(owner, server); auto goptions = Database::get_global_options(owner); coptions.col() = get_first_non_empty(coptions.col(), soptions.col()); coptions.col() = get_first_non_empty(coptions.col(), soptions.col()); coptions.col() = get_first_non_empty(coptions.col(), soptions.col(), goptions.col()); return coptions; } std::string Database::store_muc_message(const std::string& owner, const std::string& chan_name, const std::string& server_name, Database::time_point date, const std::string& body, const std::string& nick) { auto line = Database::muc_log_lines.row(); auto uuid = Database::gen_uuid(); line.col() = uuid; line.col() = owner; line.col() = chan_name; line.col() = server_name; line.col() = std::chrono::duration_cast(date.time_since_epoch()).count(); line.col() = body; line.col() = nick; line.save(Database::db); return uuid; } std::vector Database::get_muc_logs(const std::string& owner, const std::string& chan_name, const std::string& server, int limit, const std::string& start, const std::string& end) { auto request = Database::muc_log_lines.select(); request.where() << Database::Owner{} << "=" << owner << \ " and " << Database::IrcChanName{} << "=" << chan_name << \ " and " << Database::IrcServerName{} << "=" << server; if (!start.empty()) { const auto start_time = utils::parse_datetime(start); if (start_time != -1) request << " and " << Database::Date{} << ">=" << start_time; } if (!end.empty()) { const auto end_time = utils::parse_datetime(end); if (end_time != -1) request << " and " << Database::Date{} << "<=" << end_time; } request.order_by() << Id{} << " DESC "; if (limit >= 0) request.limit() << limit; auto result = request.execute(*Database::db); return {result.crbegin(), result.crend()}; } void Database::add_roster_item(const std::string& local, const std::string& remote) { auto roster_item = Database::roster.row(); roster_item.col() = local; roster_item.col() = remote; roster_item.save(Database::db); } void Database::delete_roster_item(const std::string& local, const std::string& remote) { Query query("DELETE FROM "s + Database::roster.get_name()); query << " WHERE " << Database::RemoteJid{} << "=" << remote << \ " AND " << Database::LocalJid{} << "=" << local; // query.execute(*Database::db); } bool Database::has_roster_item(const std::string& local, const std::string& remote) { auto query = Database::roster.select(); query.where() << Database::LocalJid{} << "=" << local << \ " and " << Database::RemoteJid{} << "=" << remote; auto res = query.execute(*Database::db); return !res.empty(); } std::vector Database::get_contact_list(const std::string& local) { auto query = Database::roster.select(); query.where() << Database::LocalJid{} << "=" << local; return query.execute(*Database::db); } std::vector Database::get_full_roster() { auto query = Database::roster.select(); return query.execute(*Database::db); } void Database::close() { Database::db = nullptr; } std::string Database::gen_uuid() { char uuid_str[37]; uuid_t uuid; uuid_generate(uuid); uuid_unparse(uuid, uuid_str); return uuid_str; } #endif