summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bridge/bridge.cpp10
-rw-r--r--src/bridge/bridge.hpp4
-rw-r--r--src/database/database.cpp51
-rw-r--r--src/database/database.hpp15
-rw-r--r--src/irc/irc_client.cpp2
-rw-r--r--src/xmpp/biboumi_component.cpp103
-rw-r--r--src/xmpp/biboumi_component.hpp6
7 files changed, 186 insertions, 5 deletions
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp
index 27726e4..33006c3 100644
--- a/src/bridge/bridge.cpp
+++ b/src/bridge/bridge.cpp
@@ -1081,6 +1081,16 @@ void Bridge::send_xmpp_invitation(const Iid& iid, const std::string& author)
this->xmpp.send_invitation(std::to_string(iid), this->user_jid + "/" + resource, author);
}
+void Bridge::on_irc_client_connected(const std::string& hostname)
+{
+ this->xmpp.on_irc_client_connected(hostname, this->user_jid);
+}
+
+void Bridge::on_irc_client_disconnected(const std::string& hostname)
+{
+ this->xmpp.on_irc_client_disconnected(hostname, this->user_jid);
+}
+
void Bridge::set_preferred_from_jid(const std::string& nick, const std::string& full_jid)
{
auto it = this->preferred_user_from.find(nick);
diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp
index 496b439..c10631b 100644
--- a/src/bridge/bridge.hpp
+++ b/src/bridge/bridge.hpp
@@ -201,6 +201,8 @@ public:
void send_xmpp_ping_request(const std::string& nick, const std::string& hostname,
const std::string& id);
void send_xmpp_invitation(const Iid& iid, const std::string& author);
+ void on_irc_client_connected(const std::string& hostname);
+ void on_irc_client_disconnected(const std::string& hostname);
/**
* Misc
@@ -301,8 +303,8 @@ private:
using ChannelKey = std::tuple<ChannelName, IrcHostname>;
public:
std::map<ChannelKey, std::set<Resource>> resources_in_chan;
-private:
std::map<IrcHostname, std::set<Resource>> resources_in_server;
+private:
/**
* Manage which resource is in which channel
*/
diff --git a/src/database/database.cpp b/src/database/database.cpp
index 92f7682..85c675e 100644
--- a/src/database/database.cpp
+++ b/src/database/database.cpp
@@ -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
diff --git a/src/database/database.hpp b/src/database/database.hpp
index b5f2ff0..c00c938 100644
--- a/src/database/database.hpp
+++ b/src/database/database.hpp
@@ -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:
diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp
index 67221c5..46dbdbe 100644
--- a/src/irc/irc_client.cpp
+++ b/src/irc/irc_client.cpp
@@ -297,6 +297,7 @@ void IrcClient::on_connected()
#endif
this->send_gateway_message("Connected to IRC server"s + (this->use_tls ? " (encrypted)": "") + ".");
this->send_pending_data();
+ this->bridge.on_irc_client_connected(this->get_hostname());
}
void IrcClient::on_connection_close(const std::string& error_msg)
@@ -309,6 +310,7 @@ void IrcClient::on_connection_close(const std::string& error_msg)
const IrcMessage error{"ERROR", {message}};
this->on_error(error);
log_warning(message);
+ this->bridge.on_irc_client_disconnected(this->get_hostname());
}
IrcChannel* IrcClient::get_channel(const std::string& n)
diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp
index 1c7cd92..d1c75d0 100644
--- a/src/xmpp/biboumi_component.cpp
+++ b/src/xmpp/biboumi_component.cpp
@@ -83,6 +83,14 @@ void BiboumiComponent::shutdown()
{
for (auto& pair: this->bridges)
pair.second->shutdown("Gateway shutdown");
+#ifdef USE_DATABASE
+ for (const Database::RosterItem& roster_item: Database::get_full_roster())
+ {
+ this->send_presence_to_contact(roster_item.col<Database::LocalJid>(),
+ roster_item.col<Database::RemoteJid>(),
+ "unavailable");
+ }
+#endif
}
void BiboumiComponent::clean()
@@ -160,10 +168,47 @@ 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 || bridge->find_irc_client(iid.get_server()))
+ 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")
+ {
+ this->send_presence_to_contact(to_str, from.bare(), "unavailable", id);
+ this->send_presence_to_contact(to_str, from.bare(), "unsubscribed");
+ this->send_presence_to_contact(to_str, from.bare(), "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 == "probe")
+ {
+ if ((iid.type == Iid::Type::Server && bridge->find_irc_client(iid.get_server()))
+ || iid.type == Iid::Type::None)
+ {
+#ifdef USE_DATABASE
+ if (Database::has_roster_item(to_str, from.bare()))
+#endif
+ this->send_presence_to_contact(to_str, from.bare(), "");
+#ifdef USE_DATABASE
+ else // rfc 6121 4.3.2.1
+ this->send_presence_to_contact(to_str, from.bare(), "unsubscribed");
+#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)
+ if (iid.type == Iid::Type::None)
+ this->send_presence_to_contact(to_str, from.bare(), "");
}
-
}
else
{
@@ -979,3 +1024,55 @@ 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::on_irc_client_connected(const std::string& irc_hostname, const std::string& jid)
+{
+#ifdef USE_DATABASE
+ 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, "");
+#endif
+}
+
+void BiboumiComponent::on_irc_client_disconnected(const std::string& irc_hostname, const std::string& jid)
+{
+#ifdef USE_DATABASE
+ 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");
+#endif
+}
+
+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>();
+ // In response, we will receive a presence indicating the
+ // contact is online, to which we will respond with our own
+ // presence.
+ // If the contact removed us from their roster while we were
+ // offline, we will receive an unsubscribed presence, letting us
+ // stay in sync.
+ this->send_presence_to_contact(this->get_served_hostname(), remote_jid, "probe");
+ }
+#endif
+}
diff --git a/src/xmpp/biboumi_component.hpp b/src/xmpp/biboumi_component.hpp
index 87311f9..e5547f9 100644
--- a/src/xmpp/biboumi_component.hpp
+++ b/src/xmpp/biboumi_component.hpp
@@ -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,10 @@ 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="");
+ void on_irc_client_connected(const std::string& irc_hostname, const std::string& jid);
+ void on_irc_client_disconnected(const std::string& irc_hostname, const std::string& jid);
+
/**
* Handle the various stanza types
*/