Commit 7376831b authored by louiz’'s avatar louiz’

Add get-irc-connection-info adhoc command

fix #3171
parent 50d7590d
......@@ -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
......
......@@ -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;
......
......@@ -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:
/**
......
......@@ -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
......
#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();
}
......@@ -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);
......@@ -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);
......
......@@ -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
......
......@@ -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'),
)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment