summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/biboumi.1.rst9
-rw-r--r--louloulibs/network/tcp_socket_handler.cpp12
-rw-r--r--louloulibs/network/tcp_socket_handler.hpp3
-rw-r--r--src/bridge/bridge.hpp4
-rw-r--r--src/xmpp/biboumi_adhoc_commands.cpp67
-rw-r--r--src/xmpp/biboumi_adhoc_commands.hpp2
-rw-r--r--src/xmpp/biboumi_component.cpp6
-rw-r--r--src/xmpp/biboumi_component.hpp2
-rw-r--r--tests/end_to_end/__main__.py42
9 files changed, 141 insertions, 6 deletions
diff --git a/doc/biboumi.1.rst b/doc/biboumi.1.rst
index 8f5eb9b..49c0fe4 100644
--- a/doc/biboumi.1.rst
+++ b/doc/biboumi.1.rst
@@ -488,13 +488,18 @@ On the gateway itself (e.g on the JID biboumi.example.com):
On a server JID (e.g on the JID chat.freenode.org@biboumi.example.com)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Configure: Lets each user configure some options that applies to the
+- configure: Lets each user configure some options that applies to the
concerned IRC server.
+- get-irc-connection-info: Returns some information about the IRC server,
+ for the executing user. It lets the user know if they are connected to
+ this server, from what port, with or without TLS, and it gives the list
+ of joined IRC channel, with a detailed list of which resource is in which
+ channel.
On a channel JID (e.g on the JID #test%chat.freenode.org@biboumi.example.com)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Configure: Lets each user configure some options that applies to the
+- configure: Lets each user configure some options that applies to the
concerned IRC channel. Some of these options, if not configured for a
specific channel, defaults to the value configured at the IRC server
level. For example the encoding can be specified for both the channel
diff --git a/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp
index 9decee1..1dddde5 100644
--- a/louloulibs/network/tcp_socket_handler.cpp
+++ b/louloulibs/network/tcp_socket_handler.cpp
@@ -179,6 +179,8 @@ void TCPSocketHandler::connect(const std::string& address, const std::string& po
if (this->use_tls)
this->start_tls();
#endif
+ this->connection_date = std::chrono::system_clock::now();
+
this->on_connected();
return ;
}
@@ -397,6 +399,16 @@ bool TCPSocketHandler::is_connecting() const
return this->connecting || this->resolver.is_resolving();
}
+bool TCPSocketHandler::is_using_tls() const
+{
+ return this->use_tls;
+}
+
+std::string TCPSocketHandler::get_port() const
+{
+ return this->port;
+}
+
void* TCPSocketHandler::get_receive_buffer(const size_t) const
{
return nullptr;
diff --git a/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp
index 7bbe4d4..6c4235e 100644
--- a/louloulibs/network/tcp_socket_handler.hpp
+++ b/louloulibs/network/tcp_socket_handler.hpp
@@ -106,6 +106,9 @@ public:
#endif
bool is_connected() const override final;
bool is_connecting() const;
+ bool is_using_tls() const;
+ std::string get_port() const;
+ std::chrono::system_clock::time_point connection_date;
private:
/**
diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp
index b2432f0..18ebfeb 100644
--- a/src/bridge/bridge.hpp
+++ b/src/bridge/bridge.hpp
@@ -248,10 +248,12 @@ private:
* a IRCServerNotConnected error in that case.
*/
IrcClient* get_irc_client(const std::string& hostname);
+public:
/**
* Idem, but returns nullptr if the server does not exist.
*/
IrcClient* find_irc_client(const std::string& hostname) const;
+private:
/**
* The bare JID of the user associated with this bridge. Messages from/to this
* JID are only managed by this bridge.
@@ -293,7 +295,9 @@ private:
using ChannelName = std::string;
using IrcHostname = std::string;
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;
/**
* Manage which resource is in which channel
diff --git a/src/xmpp/biboumi_adhoc_commands.cpp b/src/xmpp/biboumi_adhoc_commands.cpp
index af7e473..87b8d96 100644
--- a/src/xmpp/biboumi_adhoc_commands.cpp
+++ b/src/xmpp/biboumi_adhoc_commands.cpp
@@ -1,10 +1,12 @@
#include <xmpp/biboumi_adhoc_commands.hpp>
#include <xmpp/biboumi_component.hpp>
+#include <utils/scopeguard.hpp>
+#include <bridge/bridge.hpp>
#include <config/config.hpp>
#include <utils/string.hpp>
#include <utils/split.hpp>
#include <xmpp/jid.hpp>
-#include <algorithm>
+#include <iomanip>
#include <biboumi.h>
@@ -720,3 +722,66 @@ void DisconnectUserFromServerStep3(XmppComponent& xmpp_component, AdhocSession&
note.set_inner(msg);
command_node.add_child(std::move(note));
}
+
+void GetIrcConnectionInfoStep1(XmppComponent& component, AdhocSession& session, XmlNode& command_node)
+{
+ BiboumiComponent& biboumi_component = static_cast<BiboumiComponent&>(component);
+
+ const Jid owner(session.get_owner_jid());
+ const Jid target(session.get_target_jid());
+
+ std::string message{};
+
+ // As the function is exited, set the message in the response.
+ utils::ScopeGuard sg([&message, &command_node]()
+ {
+ command_node.delete_all_children();
+ XmlNode note("note");
+ note["type"] = "info";
+ note.set_inner(message);
+ command_node.add_child(std::move(note));
+ });
+
+ Bridge* bridge = biboumi_component.get_user_bridge(owner.bare());
+ if (!bridge)
+ {
+ message = "You are not connected to anything.";
+ return;
+ }
+
+ std::string hostname;
+ if ((hostname = Config::get("fixed_irc_server", "")).empty())
+ hostname = target.local;
+
+ IrcClient* irc = bridge->find_irc_client(hostname);
+ if (!irc || !irc->is_connected())
+ {
+ message = "You are not connected to the IRC server "s + hostname;
+ return;
+ }
+
+ std::ostringstream ss;
+ ss << "Connected to IRC server " << irc->get_hostname() << " on port " << irc->get_port();
+ if (irc->is_using_tls())
+ ss << " (using TLS)";
+ const std::time_t now_c = std::chrono::system_clock::to_time_t(irc->connection_date);
+ ss << " since " << std::put_time(std::localtime(&now_c), "%F %T");
+ ss << " (" << std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - irc->connection_date).count() << " seconds ago).";
+
+ for (const auto& it: bridge->resources_in_chan)
+ {
+ const auto& channel_key = it.first;
+ const auto& irc_hostname = std::get<1>(channel_key);
+ const auto& resources = it.second;
+
+ if (irc_hostname == irc->get_hostname() && !resources.empty())
+ {
+ const auto& channel_name = std::get<0>(channel_key);
+ ss << "\n" << channel_name << " from " << resources.size() << " resource" << (resources.size() > 1 ? "s": "") << ": ";
+ for (const auto& resource: resources)
+ ss << resource << " ";
+ }
+ }
+
+ message = ss.str();
+}
diff --git a/src/xmpp/biboumi_adhoc_commands.hpp b/src/xmpp/biboumi_adhoc_commands.hpp
index 7be5509..b5fce61 100644
--- a/src/xmpp/biboumi_adhoc_commands.hpp
+++ b/src/xmpp/biboumi_adhoc_commands.hpp
@@ -22,3 +22,5 @@ void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& co
void DisconnectUserFromServerStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node);
void DisconnectUserFromServerStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node);
void DisconnectUserFromServerStep3(XmppComponent&, AdhocSession& session, XmlNode& command_node);
+
+void GetIrcConnectionInfoStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node);
diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp
index f3405df..4398562 100644
--- a/src/xmpp/biboumi_component.cpp
+++ b/src/xmpp/biboumi_component.cpp
@@ -63,6 +63,12 @@ BiboumiComponent::BiboumiComponent(std::shared_ptr<Poller> poller, const std::st
this->adhoc_commands_handler.add_command("disconnect-from-irc-server", {{&DisconnectUserFromServerStep1, &DisconnectUserFromServerStep2, &DisconnectUserFromServerStep3}, "Disconnect from the selected IRC servers", false});
this->adhoc_commands_handler.add_command("reload", {{&Reload}, "Reload biboumi’s configuration", true});
+ AdhocCommand get_irc_connection_info{{&GetIrcConnectionInfoStep1}, "Returns various information about your connection to this IRC server.", false};
+ if (!Config::get("fixed_irc_server", "").empty())
+ this->adhoc_commands_handler.add_command("get-irc-connection-info", get_irc_connection_info);
+ else
+ this->irc_server_adhoc_commands_handler.add_command("get-irc-connection-info", get_irc_connection_info);
+
#ifdef USE_DATABASE
AdhocCommand configure_server_command({&ConfigureIrcServerStep1, &ConfigureIrcServerStep2}, "Configure a few settings for that IRC server", false);
AdhocCommand configure_global_command({&ConfigureGlobalStep1, &ConfigureGlobalStep2}, "Configure a few settings", false);
diff --git a/src/xmpp/biboumi_component.hpp b/src/xmpp/biboumi_component.hpp
index d5b87e9..999001f 100644
--- a/src/xmpp/biboumi_component.hpp
+++ b/src/xmpp/biboumi_component.hpp
@@ -101,13 +101,13 @@ public:
const std::string& queryid);
#endif
-private:
/**
* Return the bridge associated with the bare JID. Create a new one
* if none already exist.
*/
Bridge* get_user_bridge(const std::string& user_jid);
+private:
/**
* A map of id -> callback. When we want to wait for an iq result, we add
* the callback to this map, with the iq id as the key. When an iq result
diff --git a/tests/end_to_end/__main__.py b/tests/end_to_end/__main__.py
index a7a5f69..239f044 100644
--- a/tests/end_to_end/__main__.py
+++ b/tests/end_to_end/__main__.py
@@ -631,7 +631,7 @@ if __name__ == '__main__':
handshake_sequence(),
partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
- "/iq/disco_items:query/disco_items:item[3]")),
+ "/iq/disco_items:query/disco_items:item[5]")),
], conf='fixed_server'),
Scenario("list_admin_adhoc_fixed_server",
[
@@ -647,7 +647,7 @@ if __name__ == '__main__':
handshake_sequence(),
partial(send_stanza, "<iq type='get' id='idwhatever' from='{jid_one}/{resource_one}' to='{irc_host_one}@{biboumi_host}'><query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands' /></iq>"),
partial(expect_stanza, ("/iq[@type='result']/disco_items:query[@node='http://jabber.org/protocol/commands']",
- "/iq/disco_items:query/disco_items:item[1]")),
+ "/iq/disco_items:query/disco_items:item[2]")),
]),
Scenario("list_adhoc_irc_fixed_server",
[
@@ -1798,6 +1798,44 @@ if __name__ == '__main__':
partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"),
partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"),
]),
+ Scenario("get_irc_connection_info",
+ [
+ handshake_sequence(),
+
+ partial(log_message, "Not connected yet"),
+ partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"),
+ partial(expect_stanza, "/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"),
+
+ partial(log_message, "Join one room"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza, "<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"),
+ partial(expect_stanza, r"/iq/commands:command/commands:note[re:test(text(), 'Connected to IRC server irc.localhost on port 6667 since \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \(\d+ seconds ago\)\.\n#foo from 1 resource: {resource_one}.*')]"),
+ ]),
+ Scenario("get_irc_connection_info_fixed",
+ [
+ handshake_sequence(),
+
+ partial(log_message, "Not connected yet"),
+ partial(send_stanza, "<iq type='set' id='command1' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"),
+ partial(expect_stanza, "/iq/commands:command/commands:note[text()='You are not connected to the IRC server irc.localhost']"),
+
+ partial(log_message, "Join one room"),
+ partial(send_stanza,
+ "<presence from='{jid_one}/{resource_one}' to='#foo@{biboumi_host}/{nick_one}' />"),
+ connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
+ partial(expect_stanza, "/message"),
+ partial(expect_stanza, "/presence"),
+ partial(expect_stanza, "/message"),
+
+ partial(send_stanza, "<iq type='set' id='command2' from='{jid_one}/{resource_one}' to='{biboumi_host}'><command xmlns='http://jabber.org/protocol/commands' node='get-irc-connection-info' action='execute' /></iq>"),
+ partial(expect_stanza, r"/iq/commands:command/commands:note[re:test(text(), 'Connected to IRC server irc.localhost on port 6667 since \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \(\d+ seconds ago\)\.\n#foo from 1 resource: {resource_one}.*')]"),
+ ], conf='fixed_server'),
)