From 4e27298b3a6389781893589b37f66260d6a34707 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 31 May 2014 17:06:36 +0200 Subject: Add an ad-hoc command to disconnect some users --- src/xmpp/adhoc_command.cpp | 112 +++++++++++++++++++++++++++++++++++- src/xmpp/adhoc_command.hpp | 10 +++- src/xmpp/adhoc_commands_handler.cpp | 23 +++++++- src/xmpp/adhoc_commands_handler.hpp | 9 ++- src/xmpp/adhoc_session.hpp | 4 +- src/xmpp/xmpp_component.cpp | 23 +++++++- src/xmpp/xmpp_component.hpp | 12 +++- 7 files changed, 180 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/xmpp/adhoc_command.cpp b/src/xmpp/adhoc_command.cpp index b880533..a0d99a0 100644 --- a/src/xmpp/adhoc_command.cpp +++ b/src/xmpp/adhoc_command.cpp @@ -1,4 +1,7 @@ #include +#include + +#include using namespace std::string_literals; @@ -13,7 +16,12 @@ AdhocCommand::~AdhocCommand() { } -void PingStep1(AdhocSession&, XmlNode& command_node) +bool AdhocCommand::is_admin_only() const +{ + return this->admin_only; +} + +void PingStep1(XmppComponent* xmpp_component, AdhocSession&, XmlNode& command_node) { XmlNode note("note"); note["type"] = "info"; @@ -22,7 +30,7 @@ void PingStep1(AdhocSession&, XmlNode& command_node) command_node.add_child(std::move(note)); } -void HelloStep1(AdhocSession&, XmlNode& command_node) +void HelloStep1(XmppComponent* xmpp_component, AdhocSession&, XmlNode& command_node) { XmlNode x("jabber:x:data:x"); x["type"] = "form"; @@ -47,7 +55,7 @@ void HelloStep1(AdhocSession&, XmlNode& command_node) command_node.add_child(std::move(x)); } -void HelloStep2(AdhocSession& session, XmlNode& command_node) +void HelloStep2(XmppComponent* xmpp_component, AdhocSession& session, XmlNode& command_node) { // Find out if the name was provided in the form. XmlNode* x = command_node.get_child("x", "jabber:x:data"); @@ -80,3 +88,101 @@ void HelloStep2(AdhocSession& session, XmlNode& command_node) // anyway. But this is for the example. session.terminate(); } + +void DisconnectUserStep1(XmppComponent* xmpp_component, AdhocSession& session, XmlNode& command_node) +{ + XmlNode x("jabber:x:data:x"); + x["type"] = "form"; + XmlNode title("title"); + title.set_inner("Disconnect a user from the gateway"); + title.close(); + x.add_child(std::move(title)); + XmlNode instructions("instructions"); + instructions.set_inner("Choose a user JID and a quit message"); + instructions.close(); + x.add_child(std::move(instructions)); + XmlNode jids_field("field"); + jids_field["var"] = "jids"; + jids_field["type"] = "list-multi"; + jids_field["label"] = "The JIDs to disconnect"; + XmlNode required("required"); + required.close(); + jids_field.add_child(std::move(required)); + for (Bridge* bridge: xmpp_component->get_bridges()) + { + XmlNode option("option"); + option["label"] = bridge->get_jid(); + XmlNode value("value"); + value.set_inner(bridge->get_jid()); + value.close(); + option.add_child(std::move(value)); + option.close(); + jids_field.add_child(std::move(option)); + } + jids_field.close(); + x.add_child(std::move(jids_field)); + + XmlNode message_field("field"); + message_field["var"] = "quit-message"; + message_field["type"] = "text-single"; + message_field["label"] = "Quit message"; + XmlNode message_value("value"); + message_value.set_inner("Disconnected by admin"); + message_value.close(); + message_field.add_child(std::move(message_value)); + message_field.close(); + x.add_child(std::move(message_field)); + x.close(); + command_node.add_child(std::move(x)); +} + +void DisconnectUserStep2(XmppComponent* xmpp_component, AdhocSession& session, XmlNode& command_node) +{ + // Find out if the jids, and the quit message are provided in the form. + std::string quit_message; + XmlNode* x = command_node.get_child("x", "jabber:x:data"); + if (x) + { + XmlNode* message_field = nullptr; + XmlNode* jids_field = nullptr; + for (XmlNode* field: x->get_children("field", "jabber:x:data")) + if (field->get_tag("var") == "jids") + jids_field = field; + else if (field->get_tag("var") == "quit-message") + message_field = field; + if (message_field) + { + XmlNode* value = message_field->get_child("value", "jabber:x:data"); + if (value) + quit_message = value->get_inner(); + } + if (jids_field) + { + std::size_t num = 0; + for (XmlNode* value: jids_field->get_children("value", "jabber:x:data")) + { + Bridge* bridge = xmpp_component->find_user_bridge(value->get_inner()); + if (bridge) + { + bridge->shutdown(quit_message); + num++; + } + } + command_node.delete_all_children(); + + XmlNode note("note"); + note["type"] = "info"; + if (num == 0) + note.set_inner("No user were disconnected."); + else if (num == 1) + note.set_inner("1 user has been disconnected."); + else + note.set_inner(std::to_string(num) + " users have been disconnected."); + note.close(); + command_node.add_child(std::move(note)); + } + } + // TODO insert an error telling the values are missing. + session.terminate(); +} + diff --git a/src/xmpp/adhoc_command.hpp b/src/xmpp/adhoc_command.hpp index 59a3cd6..60f7d6c 100644 --- a/src/xmpp/adhoc_command.hpp +++ b/src/xmpp/adhoc_command.hpp @@ -23,6 +23,8 @@ public: const std::string name; + bool is_admin_only() const; + private: /** * A command may have one or more steps. Each step is a different @@ -33,8 +35,10 @@ private: const bool admin_only; }; -void PingStep1(AdhocSession& session, XmlNode& command_node); -void HelloStep1(AdhocSession& session, XmlNode& command_node); -void HelloStep2(AdhocSession& session, XmlNode& command_node); +void PingStep1(XmppComponent*, AdhocSession& session, XmlNode& command_node); +void HelloStep1(XmppComponent*, AdhocSession& session, XmlNode& command_node); +void HelloStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node); +void DisconnectUserStep1(XmppComponent*, AdhocSession& session, XmlNode& command_node); +void DisconnectUserStep2(XmppComponent*, AdhocSession& session, XmlNode& command_node); #endif // ADHOC_COMMAND_HPP diff --git a/src/xmpp/adhoc_commands_handler.cpp b/src/xmpp/adhoc_commands_handler.cpp index a669ca1..8657944 100644 --- a/src/xmpp/adhoc_commands_handler.cpp +++ b/src/xmpp/adhoc_commands_handler.cpp @@ -2,13 +2,17 @@ #include #include +#include +#include #include -AdhocCommandsHandler::AdhocCommandsHandler(): +AdhocCommandsHandler::AdhocCommandsHandler(XmppComponent* xmpp_component): + xmpp_component(xmpp_component), commands{ {"ping", AdhocCommand({&PingStep1}, "Do a ping", false)}, - {"hello", AdhocCommand({&HelloStep1, &HelloStep2}, "Receive a custom greeting", false)} + {"hello", AdhocCommand({&HelloStep1, &HelloStep2}, "Receive a custom greeting", false)}, + {"disconnect-user", AdhocCommand({&DisconnectUserStep1, &DisconnectUserStep2}, "Disconnect a user from the gateway", true)} } { } @@ -31,6 +35,8 @@ XmlNode&& AdhocCommandsHandler::handle_request(const std::string& executor_jid, action = "execute"; command_node.del_tag("action"); + Jid jid(executor_jid); + const std::string node = command_node.get_tag("node"); auto command_it = this->commands.find(node); if (command_it == this->commands.end()) @@ -43,6 +49,17 @@ XmlNode&& AdhocCommandsHandler::handle_request(const std::string& executor_jid, error.close(); command_node.add_child(std::move(error)); } + else if (command_it->second.is_admin_only() && + Config::get("admin", "") != jid.local + "@" + jid.domain) + { + XmlNode error(ADHOC_NS":error"); + error["type"] = "cancel"; + XmlNode condition(STANZA_NS":forbidden"); + condition.close(); + error.add_child(std::move(condition)); + error.close(); + command_node.add_child(std::move(error)); + } else { std::string sessionid = command_node.get_tag("sessionid"); @@ -74,7 +91,7 @@ XmlNode&& AdhocCommandsHandler::handle_request(const std::string& executor_jid, // execute the step AdhocSession& session = session_it->second; const AdhocStep& step = session.get_next_step(); - step(session, command_node); + step(this->xmpp_component, session, command_node); if (session.remaining_steps() == 0 || session.is_terminated()) { diff --git a/src/xmpp/adhoc_commands_handler.hpp b/src/xmpp/adhoc_commands_handler.hpp index 6e00188..f443325 100644 --- a/src/xmpp/adhoc_commands_handler.hpp +++ b/src/xmpp/adhoc_commands_handler.hpp @@ -13,10 +13,12 @@ #include #include +class XmppComponent; + class AdhocCommandsHandler { public: - explicit AdhocCommandsHandler(); + explicit AdhocCommandsHandler(XmppComponent* xmpp_component); ~AdhocCommandsHandler(); /** * Returns the list of available commands. @@ -36,6 +38,11 @@ public: */ XmlNode&& handle_request(const std::string& executor_jid, XmlNode command_node); private: + /** + * A pointer to the XmppComponent, to access to basically anything in the + * gateway. + */ + XmppComponent* xmpp_component; /** * The list of all available commands. */ diff --git a/src/xmpp/adhoc_session.hpp b/src/xmpp/adhoc_session.hpp index 111b43b..ddfb2fe 100644 --- a/src/xmpp/adhoc_session.hpp +++ b/src/xmpp/adhoc_session.hpp @@ -6,6 +6,8 @@ #include #include +class XmppComponent; + class AdhocCommand; class AdhocSession; @@ -16,7 +18,7 @@ class AdhocSession; * TODO fix this: * It also must call one of step_passed(), cancel() etc on the AdhocSession object. */ -typedef std::function AdhocStep; +typedef std::function AdhocStep; class AdhocSession { diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 9547e3a..81a3b4f 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -41,7 +41,8 @@ XmppComponent::XmppComponent(std::shared_ptr poller, const std::string& served_hostname(hostname), secret(secret), authenticated(false), - doc_open(false) + doc_open(false), + adhoc_commands_handler(this) { this->parser.add_stream_open_callback(std::bind(&XmppComponent::on_remote_stream_open, this, std::placeholders::_1)); @@ -543,6 +544,26 @@ Bridge* XmppComponent::get_user_bridge(const std::string& user_jid) } } +Bridge* XmppComponent::find_user_bridge(const std::string& user_jid) +{ + try + { + return this->bridges.at(user_jid).get(); + } + catch (const std::out_of_range& exception) + { + return nullptr; + } +} + +std::list XmppComponent::get_bridges() const +{ + std::list res; + for (auto it = this->bridges.begin(); it != this->bridges.end(); ++it) + res.push_back(it->second.get()); + return res; +} + void* XmppComponent::get_receive_buffer(const size_t size) const { return this->parser.get_buffer(size); diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index e3e24e0..5de471c 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -41,6 +41,16 @@ public: void on_connection_close() override final; void parse_in_buffer(const size_t size) override final; + /** + * Returns the bridge for the given user. If it does not exist, return + * nullptr. + */ + Bridge* find_user_bridge(const std::string& user_jid); + /** + * Return a list of all the managed bridges. + */ + std::list get_bridges() const; + /** * Returns a unique id, to be used in the 'id' element of our iq stanzas. */ @@ -228,6 +238,7 @@ private: bool doc_open; std::unordered_map> stanza_handlers; + AdhocCommandsHandler adhoc_commands_handler; /** * One bridge for each user of the component. Indexed by the user's full @@ -235,7 +246,6 @@ private: */ std::unordered_map> bridges; - AdhocCommandsHandler adhoc_commands_handler; XmppComponent(const XmppComponent&) = delete; XmppComponent(XmppComponent&&) = delete; XmppComponent& operator=(const XmppComponent&) = delete; -- cgit v1.2.3