From 43cc60e4a9e2859fdf67c89e58ee18cf7571f186 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 27 Dec 2013 15:34:58 +0100 Subject: Handle nickname conflicts by sending the correct XMPP error presence --- src/bridge/bridge.cpp | 5 +++++ src/bridge/bridge.hpp | 1 + src/irc/irc_client.cpp | 13 +++++++++++++ src/irc/irc_client.hpp | 6 ++++++ src/xmpp/xmpp_component.cpp | 29 +++++++++++++++++++++++++++-- src/xmpp/xmpp_component.hpp | 6 ++++++ 6 files changed, 58 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index c93d710..7f245db 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -216,3 +216,8 @@ void Bridge::kick_muc_user(Iid&& iid, const std::string& target, const std::stri { this->xmpp->kick_user(iid.chan + "%" + iid.server, target, reason, author, this->user_jid); } + +void Bridge::send_nickname_conflict_error(const Iid& iid, const std::string& nickname) +{ + this->xmpp->send_nickname_conflict_error(iid.chan + "%" + iid.server, nickname, this->user_jid); +} diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index 1e1149b..b5bee9e 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -89,6 +89,7 @@ public: */ 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); + void send_nickname_conflict_error(const Iid& iid, const std::string& nickname); /** * Misc diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index afdc629..10a5b12 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -303,6 +303,19 @@ void IrcClient::on_erroneous_nickname(const IrcMessage& message) this->send_gateway_message(error_msg + ": " + message.arguments[1], message.prefix); } +void IrcClient::on_nickname_conflict(const IrcMessage& message) +{ + const std::string nickname = message.arguments[1]; + this->on_generic_error(message); + for (auto it = this->channels.begin(); it != this->channels.end(); ++it) + { + Iid iid; + iid.chan = it->first; + iid.server = this->hostname; + this->bridge->send_nickname_conflict_error(iid, nickname); + } +} + void IrcClient::on_generic_error(const IrcMessage& message) { const std::string error_msg = message.arguments.size() >= 3 ? diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index e695e53..eced4b6 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -148,6 +148,11 @@ public: * We tried to set an invalid nickname */ void on_erroneous_nickname(const IrcMessage& message); + /** + * When the IRC servers denies our nickname because of a conflict. Send a + * presence conflict from all channels, because the name is server-wide. + */ + void on_nickname_conflict(const IrcMessage& message); /** * Handles most errors from the server by just forwarding the message to the user. */ @@ -237,6 +242,7 @@ static const std::unordered_map irc_callbacks = { {"TOPIC", &IrcClient::on_topic_received}, {"366", &IrcClient::on_channel_completely_joined}, {"432", &IrcClient::on_erroneous_nickname}, + {"433", &IrcClient::on_nickname_conflict}, {"461", &IrcClient::on_generic_error}, {"001", &IrcClient::on_welcome_message}, {"PART", &IrcClient::on_part}, diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 3c37eb1..1cc4c25 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -20,6 +20,7 @@ #define DISCO_ITEMS_NS DISCO_NS"#items" #define DISCO_INFO_NS DISCO_NS"#info" #define XHTMLIM_NS "http://jabber.org/protocol/xhtml-im" +#define STANZA_NS "urn:ietf:params:xml:ns:xmpp-stanzas" XmppComponent::XmppComponent(const std::string& hostname, const std::string& secret): served_hostname(hostname), @@ -195,8 +196,7 @@ void XmppComponent::handle_presence(const Stanza& stanza) const std::string own_nick = bridge->get_own_nick(iid); if (!own_nick.empty() && own_nick != to.resource) bridge->send_irc_nick_change(iid, to.resource); - else - bridge->join_irc_channel(iid, to.resource); + bridge->join_irc_channel(iid, to.resource); } else if (type == "unavailable") { @@ -479,3 +479,28 @@ void XmppComponent::kick_user(const std::string& muc_name, presence.close(); this->send_stanza(presence); } + +void XmppComponent::send_nickname_conflict_error(const std::string& muc_name, + const std::string& nickname, + const std::string& jid_to) +{ + Stanza presence("presence"); + presence["from"] = muc_name + "@" + this->served_hostname + "/" + nickname; + presence["to"] = jid_to; + XmlNode x("x"); + x["xmlns"] = MUC_NS; + x.close(); + presence.add_child(std::move(x)); + XmlNode error("error"); + error["by"] = muc_name + "@" + this->served_hostname; + error["type"] = "cancel"; + error["code"] = "409"; + XmlNode conflict("conflict"); + conflict["xmlns"] = STANZA_NS; + conflict.close(); + error.add_child(std::move(conflict)); + error.close(); + presence.add_child(std::move(error)); + presence.close(); + this->send_stanza(presence); +} diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index d76a2c3..63eb88f 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -107,6 +107,12 @@ public: const std::string& reason, const std::string& author, const std::string& jid_to); + /** + * Send a presence type=error with a conflict element + */ + void send_nickname_conflict_error(const std::string& muc_name, + const std::string& nickname, + const std::string& jid_to); /** * Handle the various stanza types */ -- cgit v1.2.3