~singpolyma/biboumi

84034ed3dc19f718dcc93a35dbf4c840a55efb1b — louiz’ 5 years ago b233470
Use a db roster to manage biboumi’s presence with the contacts
M src/database/database.cpp => src/database/database.cpp +50 -1
@@ 13,6 13,8 @@ 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");


void Database::open(const std::string& filename)
{


@@ 36,6 38,8 @@ void Database::open(const std::string& filename)
  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);
}




@@ 177,6 181,51 @@ std::vector<Database::MucLogLine> Database::get_muc_logs(const std::string& owne
  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<Database::LocalJid>() = local;
  roster_item.col<Database::RemoteJid>() = 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::RosterItem> 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::RosterItem> Database::get_full_roster()
{
  auto query = Database::roster.select();

  return query.execute(Database::db);
}

void Database::close()
{
  sqlite3_close_v2(Database::db);


@@ 192,4 241,4 @@ std::string Database::gen_uuid()
  return uuid_str;
}

#endif
\ No newline at end of file
#endif

M src/database/database.hpp => src/database/database.hpp +15 -0
@@ 72,6 72,11 @@ class Database
  struct Persistent: Column<bool> { static constexpr auto name = "persistent_";
    Persistent(): Column<bool>(false) {} };

  struct LocalJid: Column<std::string> { static constexpr auto name = "local"; };

  struct RemoteJid: Column<std::string> { static constexpr auto name = "remote"; };


  using MucLogLineTable = Table<Id, Uuid, Owner, IrcChanName, IrcServerName, Date, Body, Nick>;
  using MucLogLine = MucLogLineTable::RowType;



@@ 84,6 89,9 @@ class Database
  using IrcChannelOptionsTable = Table<Id, Owner, Server, Channel, EncodingOut, EncodingIn, MaxHistoryLength, Persistent, RecordHistoryOptional>;
  using IrcChannelOptions = IrcChannelOptionsTable::RowType;

  using RosterTable = Table<LocalJid, RemoteJid>;
  using RosterItem = RosterTable::RowType;

  Database() = default;
  ~Database() = default;



@@ 109,6 117,12 @@ class Database
  static std::string store_muc_message(const std::string& owner, const std::string& chan_name, const std::string& server_name,
                                       time_point date, const std::string& body, const std::string& nick);

  static void add_roster_item(const std::string& local, const std::string& remote);
  static bool has_roster_item(const std::string& local, const std::string& remote);
  static void delete_roster_item(const std::string& local, const std::string& remote);
  static std::vector<Database::RosterItem> get_contact_list(const std::string& local);
  static std::vector<Database::RosterItem> get_full_roster();

  static void close();
  static void open(const std::string& filename);



@@ 123,6 137,7 @@ class Database
  static GlobalOptionsTable global_options;
  static IrcServerOptionsTable irc_server_options;
  static IrcChannelOptionsTable irc_channel_options;
  static RosterTable roster;
  static sqlite3* db;

 private:

M src/xmpp/biboumi_component.cpp => src/xmpp/biboumi_component.cpp +58 -3
@@ 83,6 83,15 @@ void BiboumiComponent::shutdown()
{
  for (auto& pair: this->bridges)
    pair.second->shutdown("Gateway shutdown");
#ifdef USE_DATABASE
  const auto full_roster = Database::get_full_roster();
  for (const Database::RosterItem& roster_item: full_roster)
    {
      this->send_presence_to_contact(roster_item.col<Database::LocalJid>(),
                                     roster_item.col<Database::RemoteJid>(),
                                     "unavailable");
    }
#endif
}

void BiboumiComponent::clean()


@@ 160,10 169,28 @@ void BiboumiComponent::handle_presence(const Stanza& stanza)
    {
      if (type == "subscribe")
        { // Auto-accept any subscription request for an IRC server
          this->accept_subscription(to_str, from.bare());
          this->ask_subscription(to_str, from.bare());
          this->send_presence_to_contact(to_str, from.bare(), "subscribed", id);
          if (iid.type == Iid::Type::None)
            this->send_presence_to_contact(to_str, from.bare(), "");
          this->send_presence_to_contact(to_str, from.bare(), "subscribe");
#ifdef USE_DATABASE
          if (!Database::has_roster_item(to_str, from.bare()))
            Database::add_roster_item(to_str, from.bare());
#endif
        }
      else if (type == "unsubscribe")
        {
#ifdef USE_DATABASE
          const bool res = Database::has_roster_item(to_str, from.bare());
          if (res)
            Database::delete_roster_item(to_str, from.bare());
#endif
        }
      else if (type.empty())
        { // We just receive a presence from someone (as the result of a probe,
          // or a directed presence, or a normal presence change)
          this->send_presence_to_contact(to_str, from.bare(), "");
        }

    }
  else
    {


@@ 979,3 1006,31 @@ void BiboumiComponent::ask_subscription(const std::string& from, const std::stri
  presence["type"] = "subscribe";
  this->send_stanza(presence);
}

void BiboumiComponent::send_presence_to_contact(const std::string& from, const std::string& to,
                                                const std::string& type, const std::string& id)
{
  Stanza presence("presence");
  presence["from"] = from;
  presence["to"] = to;
  if (!type.empty())
    presence["type"] = type;
  if (!id.empty())
    presence["id"] = id;
  this->send_stanza(presence);
}

void BiboumiComponent::after_handshake()
{
  XmppComponent::after_handshake();

#ifdef USE_DATABASE
  const auto contacts = Database::get_contact_list(this->get_served_hostname());

  for (const Database::RosterItem& roster_item: contacts)
    {
      const auto remote_jid = roster_item.col<Database::RemoteJid>();
      this->send_presence_to_contact(this->get_served_hostname(), remote_jid, "probe");
    }
#endif
}

M src/xmpp/biboumi_component.hpp => src/xmpp/biboumi_component.hpp +3 -0
@@ 36,6 36,8 @@ public:
  BiboumiComponent& operator=(const BiboumiComponent&) = delete;
  BiboumiComponent& operator=(BiboumiComponent&&) = delete;

  void after_handshake() override final;

  /**
   * Returns the bridge for the given user. If it does not exist, return
   * nullptr.


@@ 87,6 89,7 @@ public:
  void send_invitation(const std::string& room_target, const std::string& jid_to, const std::string& author_nick);
  void accept_subscription(const std::string& from, const std::string& to);
  void ask_subscription(const std::string& from, const std::string& to);
  void send_presence_to_contact(const std::string& from, const std::string& to, const std::string& type, const std::string& id="");
  /**
   * Handle the various stanza types
   */