From 0859801230f999889d0f7356864888e8c5936cda Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 13 Nov 2013 01:24:36 +0100 Subject: Handle KICK in irc channel, both ways --- src/bridge/bridge.cpp | 12 +++++++ src/bridge/bridge.hpp | 2 ++ src/irc/irc_client.cpp | 20 ++++++++++++ src/irc/irc_client.hpp | 6 ++++ src/xmpp/xmpp_component.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++ src/xmpp/xmpp_component.hpp | 9 ++++++ 6 files changed, 128 insertions(+) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 0b26a7f..e39cdd3 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -122,6 +122,13 @@ void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick) irc->send_nick_command(new_nick); } +void Bridge::send_irc_kick(const Iid& iid, const std::string& target, const std::string& reason) +{ + IrcClient* irc = this->get_irc_client(iid.server); + if (irc) + irc->send_kick_command(iid.chan, target, reason); +} + void Bridge::send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc) { std::string utf8_body = this->sanitize_for_xmpp(body); @@ -180,3 +187,8 @@ std::string Bridge::get_own_nick(const Iid& iid) return irc->get_own_nick(); return ""; } + +void Bridge::kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author) +{ + this->xmpp->kick_user(iid.chan + "%" + iid.server, target, reason, author, this->user_jid); +} diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index 0466ee1..b2124bd 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -39,6 +39,7 @@ public: void send_private_message(const Iid& iid, const std::string& body); void leave_irc_channel(Iid&& iid, std::string&& status_message); void send_irc_nick_change(const Iid& iid, const std::string& new_nick); + void send_irc_kick(const Iid& iid, const std::string& target, const std::string& reason); /*** ** @@ -76,6 +77,7 @@ public: * true) changed his nick to new_nick */ void send_nick_change(Iid&& iid, const std::string& old_nick, const std::string& new_nick, const bool self); + void kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author); /** * Misc diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 1d2e487..82abbd9 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -113,6 +113,11 @@ void IrcClient::send_nick_command(const std::string& nick) this->send_message(IrcMessage("NICK", {nick})); } +void IrcClient::send_kick_command(const std::string& chan_name, const std::string& target, const std::string& reason) +{ + this->send_message(IrcMessage("KICK", {chan_name, target, reason})); +} + void IrcClient::send_join_command(const std::string& chan_name) { if (this->welcomed == false) @@ -311,6 +316,21 @@ void IrcClient::on_nick(const IrcMessage& message) } } +void IrcClient::on_kick(const IrcMessage& message) +{ + const std::string target = message.arguments[1]; + const std::string reason = message.arguments[2]; + const std::string chan_name = message.arguments[0]; + IrcChannel* channel = this->get_channel(chan_name); + if (channel->get_self()->nick == target) + channel->joined = false; + IrcUser author(message.prefix); + Iid iid; + iid.chan = chan_name; + iid.server = this->hostname; + this->bridge->kick_muc_user(std::move(iid), target, reason, author.nick); +} + void IrcClient::on_mode(const IrcMessage& message) { const std::string target = message.arguments[0]; diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index 07ff02c..3b22fa0 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -91,6 +91,10 @@ public: * Send the MODE irc command */ void send_mode_command(const std::string& chan_name, const std::vector& arguments); + /** + * Send the KICK irc command + */ + void send_kick_command(const std::string& chan_name, const std::string& target, const std::string& reason); /** * Forward the server message received from IRC to the XMPP component */ @@ -124,6 +128,7 @@ public: void on_welcome_message(const IrcMessage& message); void on_part(const IrcMessage& message); void on_nick(const IrcMessage& message); + void on_kick(const IrcMessage& message); void on_mode(const IrcMessage& message); /** * A mode towards our own user is received (note, that is different from a @@ -194,6 +199,7 @@ static const std::unordered_map irc_callbacks = { {"NICK", &IrcClient::on_nick}, {"MODE", &IrcClient::on_mode}, {"PING", &IrcClient::send_pong_command}, + {"KICK", &IrcClient::on_kick}, }; #endif // IRC_CLIENT_INCLUDED diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index c36a141..2d891bc 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -14,6 +14,7 @@ #define COMPONENT_NS "jabber:component:accept" #define MUC_NS "http://jabber.org/protocol/muc" #define MUC_USER_NS MUC_NS"#user" +#define MUC_ADMIN_NS MUC_NS"#admin" #define DISCO_NS "http://jabber.org/protocol/disco" #define DISCO_ITEMS_NS DISCO_NS"#items" #define DISCO_INFO_NS DISCO_NS"#info" @@ -36,6 +37,8 @@ XmppComponent::XmppComponent(const std::string& hostname, const std::string& sec std::bind(&XmppComponent::handle_presence, this,std::placeholders::_1)); this->stanza_handlers.emplace(COMPONENT_NS":message", std::bind(&XmppComponent::handle_message, this,std::placeholders::_1)); + this->stanza_handlers.emplace(COMPONENT_NS":iq", + std::bind(&XmppComponent::handle_iq, this,std::placeholders::_1)); } XmppComponent::~XmppComponent() @@ -201,6 +204,46 @@ void XmppComponent::handle_message(const Stanza& stanza) } } +void XmppComponent::handle_iq(const Stanza& stanza) +{ + Bridge* bridge = this->get_user_bridge(stanza["from"]); + Jid to(stanza["to"]); + std::string type; + try { + type = stanza["type"]; + } + catch (const AttributeNotFound&) + { return; } + if (type == "set") + { + XmlNode* query; + if ((query = stanza.get_child(MUC_ADMIN_NS":query"))) + { + XmlNode* child; + if ((child = query->get_child(MUC_ADMIN_NS":item"))) + { + std::string nick; + std::string role; + try { + nick = (*child)["nick"]; + role = (*child)["role"]; + } + catch (const AttributeNotFound&) + { return; } + if (!nick.empty() && role == "none") + { + std::string reason; + XmlNode* reason_el = child->get_child(MUC_ADMIN_NS":reason"); + if (reason_el) + reason = reason_el->get_inner(); + Iid iid(to.local); + bridge->send_irc_kick(iid, nick, reason); + } + } + } + } +} + Bridge* XmppComponent::get_user_bridge(const std::string& user_jid) { try @@ -360,3 +403,39 @@ void XmppComponent::send_nick_change(const std::string& muc_name, const std::str else this->send_user_join(muc_name, new_nick, jid_to); } + +void XmppComponent::kick_user(const std::string& muc_name, + const std::string& target, + const std::string& txt, + const std::string& author, + const std::string& jid_to) +{ + Stanza presence("presence"); + presence["from"] = muc_name + "@" + this->served_hostname + "/" + target; + presence["to"] = jid_to; + presence["type"] = "unavailable"; + XmlNode x("x"); + x["xmlns"] = MUC_USER_NS; + XmlNode item("item"); + item["affiliation"] = "none"; + item["role"] = "none"; + XmlNode actor("actor"); + actor["nick"] = author; + actor["jid"] = author; // backward compatibility with old clients + actor.close(); + item.add_child(std::move(actor)); + XmlNode reason("reason"); + reason.set_inner(txt); + reason.close(); + item.add_child(std::move(reason)); + item.close(); + x.add_child(std::move(item)); + XmlNode status("status"); + status["code"] = "307"; + status.close(); + x.add_child(std::move(status)); + x.close(); + presence.add_child(std::move(x)); + presence.close(); + this->send_stanza(presence); +} diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 84b19a9..0c68497 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -83,12 +83,21 @@ public: * Indicate that a participant changed his nick */ void send_nick_change(const std::string& muc_name, const std::string& old_nick, const std::string& new_nick, const std::string& jid_to, const bool self); + /** + * An user is kicked from a room + */ + void kick_user(const std::string& muc_name, + const std::string& target, + const std::string& reason, + const std::string& author, + const std::string& jid_to); /** * Handle the various stanza types */ void handle_handshake(const Stanza& stanza); void handle_presence(const Stanza& stanza); void handle_message(const Stanza& stanza); + void handle_iq(const Stanza& stanza); private: /** -- cgit v1.2.3