From 096a4e3bafe6e2d238e4592f57f22f19f363fcbd Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Mon, 11 Nov 2013 00:24:34 +0100 Subject: Handle nick changes, both ways --- src/bridge/bridge.cpp | 32 +++++++++++++++++++++++++++++--- src/bridge/bridge.hpp | 19 +++++++++++++++++-- src/irc/irc_client.cpp | 45 +++++++++++++++++++++++++++++++++++++++------ src/irc/irc_client.hpp | 10 +++++++++- src/xmpp/xmpp_component.cpp | 42 +++++++++++++++++++++++++++++++++++++++++- src/xmpp/xmpp_component.hpp | 4 ++++ 6 files changed, 139 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 1f394bf..d292af3 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -59,10 +59,15 @@ IrcClient* Bridge::get_irc_client(const std::string& hostname) } } -void Bridge::join_irc_channel(const Iid& iid, const std::string& username) +bool Bridge::join_irc_channel(const Iid& iid, const std::string& username) { IrcClient* irc = this->get_irc_client(iid.server, username); - irc->send_join_command(iid.chan); + if (irc->is_channel_joined(iid.chan) == false) + { + irc->send_join_command(iid.chan); + return true; + } + return false; } void Bridge::send_channel_message(const Iid& iid, const std::string& body) @@ -103,6 +108,13 @@ void Bridge::leave_irc_channel(Iid&& iid, std::string&& status_message) irc->send_part_command(iid.chan, status_message); } +void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick) +{ + IrcClient* irc = this->get_irc_client(iid.server); + if (irc) + irc->send_nick_command(new_nick); +} + 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); @@ -118,11 +130,17 @@ void Bridge::send_message(const Iid& iid, const std::string& nick, const std::st this->xmpp->send_message(iid.chan + "%" + iid.server, utf8_body, this->user_jid); } -void Bridge::send_muc_leave(Iid&& iid, std::string&& nick, std::string&& message, const bool self) +void Bridge::send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self) { this->xmpp->send_muc_leave(std::move(iid.chan) + "%" + std::move(iid.server), std::move(nick), this->sanitize_for_xmpp(message), this->user_jid, self); } +void Bridge::send_nick_change(Iid&& iid, const std::string& old_nick, const std::string& new_nick, const bool self) +{ + this->xmpp->send_nick_change(std::move(iid.chan) + "%" + std::move(iid.server), + old_nick, new_nick, this->user_jid, self); +} + void Bridge::send_xmpp_message(const std::string& from, const std::string& author, const std::string& msg) { std::string body; @@ -147,3 +165,11 @@ void Bridge::send_topic(const std::string& hostname, const std::string& chan_nam { this->xmpp->send_topic(chan_name + "%" + hostname, this->sanitize_for_xmpp(topic), this->user_jid); } + +std::string Bridge::get_own_nick(const Iid& iid) +{ + IrcClient* irc = this->get_irc_client(iid.server); + if (irc) + return irc->get_own_nick(); + return ""; +} diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index e0f4598..0466ee1 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -30,10 +30,15 @@ public: ** **/ - void join_irc_channel(const Iid& iid, const std::string& username); + /** + * Try to join an irc_channel, does nothing and return true if the channel + * was already joined. + */ + bool join_irc_channel(const Iid& iid, const std::string& username); void send_channel_message(const Iid& iid, const std::string& body); 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); /*** ** @@ -65,7 +70,17 @@ public: /** * Send an unavailable presence from this participant */ - void send_muc_leave(Iid&& iid, std::string&& nick, std::string&& message, const bool self); + void send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self); + /** + * Send presences to indicate that an user old_nick (ourself if self == + * 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); + + /** + * Misc + */ + std::string get_own_nick(const Iid& iid); private: /** diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 71b0fe2..30b1204 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -53,6 +53,12 @@ IrcChannel* IrcClient::get_channel(const std::string& name) } } +bool IrcClient::is_channel_joined(const std::string& name) +{ + IrcChannel* client = this->get_channel(name); + return client->joined; +} + std::string IrcClient::get_own_nick() const { return this->current_nick; @@ -91,6 +97,8 @@ void IrcClient::parse_in_buffer() this->on_part(message); else if (message.command == "QUIT") this->on_quit(message); + else if (message.command == "NICK") + this->on_nick(message); } } @@ -128,11 +136,9 @@ void IrcClient::send_nick_command(const std::string& nick) void IrcClient::send_join_command(const std::string& chan_name) { if (this->welcomed == false) - { - this->channels_to_join.push_back(chan_name); - return ; - } - this->send_message(IrcMessage("JOIN", {chan_name})); + this->channels_to_join.push_back(chan_name); + else + this->send_message(IrcMessage("JOIN", {chan_name})); } bool IrcClient::send_channel_message(const std::string& chan_name, const std::string& body) @@ -288,7 +294,34 @@ void IrcClient::on_quit(const IrcMessage& message) Iid iid; iid.chan = chan_name; iid.server = this->hostname; - this->bridge->send_muc_leave(std::move(iid), std::move(nick), std::move(txt), false); + this->bridge->send_muc_leave(std::move(iid), std::move(nick), txt, false); } } } + +void IrcClient::on_nick(const IrcMessage& message) +{ + const std::string new_nick = message.arguments[0]; + for (auto it = this->channels.begin(); it != this->channels.end(); ++it) + { + const std::string chan_name = it->first; + IrcChannel* channel = it->second.get(); + IrcUser* user = channel->find_user(message.prefix); + if (user) + { + std::string old_nick = user->nick; + Iid iid; + iid.chan = chan_name; + iid.server = this->hostname; + bool self = channel->get_self()->nick == old_nick; + this->bridge->send_nick_change(std::move(iid), old_nick, new_nick, self); + user->nick = new_nick; + if (self) + { + channel->get_self()->nick = new_nick; + this->current_nick = new_nick; + } + } + } +} + diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index bb51a4e..d9ea069 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -44,6 +44,10 @@ public: * Return the channel with this name, create it if it does not yet exist */ IrcChannel* get_channel(const std::string& name); + /** + * Returns true if the channel is joined + */ + bool is_channel_joined(const std::string& name); /** * Return our own nick */ @@ -67,7 +71,7 @@ public: */ void send_nick_command(const std::string& username); /** - * Send the JOIN irc command + * Send the JOIN irc command. */ void send_join_command(const std::string& chan_name); /** @@ -118,6 +122,10 @@ public: * When a PART message is received */ void on_part(const IrcMessage& message); + /** + * When a NICK message is received + */ + void on_nick(const IrcMessage& message); /** * When a QUIT message is received */ diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 7cf2ce0..e2063ba 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -167,7 +167,13 @@ void XmppComponent::handle_presence(const Stanza& stanza) if (!iid.chan.empty() && !iid.chan.empty()) { // presence toward a MUC that corresponds to an irc channel if (type.empty()) - bridge->join_irc_channel(iid, to.resource); + { + 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); + } else if (type == "unavailable") { XmlNode* status = stanza.get_child(MUC_USER_NS":status"); @@ -317,3 +323,37 @@ void XmppComponent::send_muc_leave(std::string&& muc_name, std::string&& nick, s presence.close(); this->send_stanza(presence); } + +void XmppComponent::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) +{ + Stanza presence("presence"); + presence["to"] = jid_to; + presence["from"] = muc_name + "@" + this->served_hostname + "/" + old_nick; + presence["type"] = "unavailable"; + XmlNode x("x"); + x["xmlns"] = MUC_USER_NS; + XmlNode item("item"); + item["nick"] = new_nick; + item.close(); + x.add_child(std::move(item)); + XmlNode status("status"); + status["code"] = "303"; + status.close(); + x.add_child(std::move(status)); + if (self) + { + XmlNode status2("status"); + status2["code"] = "110"; + status2.close(); + x.add_child(std::move(status2)); + } + x.close(); + presence.add_child(std::move(x)); + presence.close(); + this->send_stanza(presence); + + if (self) + this->send_self_join(muc_name, new_nick, jid_to); + else + this->send_user_join(muc_name, new_nick, jid_to); +} diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index a5127a9..84b19a9 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -79,6 +79,10 @@ public: * Send an unavailable presence for this nick */ void send_muc_leave(std::string&& muc_name, std::string&& nick, std::string&& message, const std::string& jid_to, const bool self); + /** + * 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); /** * Handle the various stanza types */ -- cgit v1.2.3