From 2d11a5f49454717c404b25825f18e696281207d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?louiz=E2=80=99?= Date: Wed, 8 Jun 2016 01:32:39 +0200 Subject: Support multiple nick session, except for IQs ref #2556 --- src/bridge/bridge.cpp | 123 +++++++++++++++++++++++++++++------------ src/bridge/bridge.hpp | 16 ++++-- src/irc/iid.cpp | 5 ++ src/irc/iid.hpp | 2 + src/irc/irc_channel.cpp | 15 ++--- src/irc/irc_client.cpp | 12 +--- src/irc/irc_client.hpp | 2 + src/xmpp/biboumi_component.cpp | 2 +- 8 files changed, 118 insertions(+), 59 deletions(-) diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index bfd5d68..95ca68e 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -201,7 +201,7 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body) { if (iid.get_server().empty()) { - for (const auto& resource: this->resources_in_chan[ChannelKey{iid.get_local(), iid.get_server()}]) + for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) this->xmpp.send_stanza_error("message", this->user_jid + "/" + resource, std::to_string(iid), "", "cancel", "remote-server-not-found", std::to_string(iid) + " is not a valid channel name. " @@ -235,7 +235,7 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body) irc->send_channel_message(iid.get_local(), action_prefix + line.substr(4) + "\01"); else irc->send_channel_message(iid.get_local(), line); - for (const auto& resource: this->resources_in_chan[ChannelKey{iid.get_local(), iid.get_server()}]) + for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) this->xmpp.send_muc_message(std::to_string(iid), irc->get_own_nick(), this->make_xmpp_body(line), this->user_jid + "/" + resource); } @@ -335,11 +335,29 @@ void Bridge::send_raw_message(const std::string& hostname, const std::string& bo irc->send_raw(body); } -void Bridge::leave_irc_channel(Iid&& iid, std::string&& status_message) +void Bridge::leave_irc_channel(Iid&& iid, std::string&& status_message, const std::string& resource) { IrcClient* irc = this->get_irc_client(iid.get_server()); - irc->send_part_command(iid.get_local(), status_message); -} + const auto key = iid.to_tuple(); + if (!this->is_resource_in_chan(key, resource)) + return ; + + const auto resources = this->number_of_resources_in_chan(key); + if (resources == 1) + irc->send_part_command(iid.get_local(), status_message); + else + { + IrcChannel* chan = irc->get_channel(iid.get_local()); + if (chan) + { + auto nick = chan->get_self()->nick; + this->remove_resource_from_chan(key, resource); + this->send_muc_leave(std::move(iid), std::move(nick), + "Biboumi note: "s + std::to_string(resources - 1) + " resources are still in this channel.", + true, resource); + } + } + } void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick) { @@ -565,7 +583,7 @@ void Bridge::send_message(const Iid& iid, const std::string& nick, const std::st const auto encoding = in_encoding_for(*this, iid); if (muc) { - for (const auto& resource: this->resources_in_chan[ChannelKey{iid.get_local(), iid.get_server()}]) + for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) { this->xmpp.send_muc_message(std::to_string(iid), nick, this->make_xmpp_body(body, encoding), this->user_jid + "/" + resource); @@ -574,17 +592,19 @@ void Bridge::send_message(const Iid& iid, const std::string& nick, const std::st else { std::string target = std::to_string(iid); - bool fulljid = false; - auto it = this->preferred_user_from.find(iid.get_local()); + const auto it = this->preferred_user_from.find(iid.get_local()); if (it != this->preferred_user_from.end()) { - target = it->second; - fulljid = true; + const auto chan_name = Iid(Jid(it->second).local).get_local(); + for (const auto& resource: this->resources_in_chan[ChannelKey{chan_name, iid.get_server()}]) + this->xmpp.send_message(it->second, this->make_xmpp_body(body, encoding), + this->user_jid + "/" + resource, "chat", true); } - for (const auto& resource: this->resources_in_chan[ChannelKey{iid.get_local(), iid.get_server()}]) + else { - this->xmpp.send_message(target, this->make_xmpp_body(body, encoding), - this->user_jid + "/" + resource, "chat", fulljid); + for (const auto& resource: this->resources_in_server[iid.get_server()]) + this->xmpp.send_message(std::to_string(iid), this->make_xmpp_body(body, encoding), + this->user_jid + "/" + resource, "chat", false); } } } @@ -596,10 +616,15 @@ void Bridge::send_presence_error(const Iid& iid, const std::string& nick, this->xmpp.send_presence_error(std::to_string(iid), nick, this->user_jid, type, condition, error_code, text); } -void Bridge::send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self) +void Bridge::send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self, const std::string& resource) { - for (const auto& resource: this->resources_in_chan[ChannelKey{iid.get_local(), iid.get_server()}]) - this->xmpp.send_muc_leave(std::to_string(iid), std::move(nick), this->make_xmpp_body(message), this->user_jid + "/" + resource, self); + if (!resource.empty()) + this->xmpp.send_muc_leave(std::to_string(iid), std::move(nick), this->make_xmpp_body(message), this->user_jid + "/" + resource, + self); + else + for (const auto& res: this->resources_in_chan[iid.to_tuple()]) + this->xmpp.send_muc_leave(std::to_string(iid), std::move(nick), this->make_xmpp_body(message), this->user_jid + "/" + res, + self); IrcClient* irc = this->find_irc_client(iid.get_server()); if (irc && irc->number_of_joined_channels() == 0) irc->send_quit_command(""); @@ -615,7 +640,7 @@ void Bridge::send_nick_change(Iid&& iid, std::string role; std::tie(role, affiliation) = get_role_affiliation_from_irc_mode(user_mode); - for (const auto& resource: this->resources_in_chan[ChannelKey{iid.get_local(), iid.get_server()}]) + for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) this->xmpp.send_nick_change(std::to_string(iid), old_nick, new_nick, affiliation, role, this->user_jid + "/" + resource, self); } @@ -640,33 +665,43 @@ void Bridge::send_xmpp_message(const std::string& from, const std::string& autho } } -void Bridge::send_user_join(const std::string& hostname, - const std::string& chan_name, - const IrcUser* user, - const char user_mode, - const bool self) +void Bridge::send_user_join(const std::string& hostname, const std::string& chan_name, + const IrcUser* user, const char user_mode, const bool self) +{ + for (const auto& resource: this->resources_in_chan[ChannelKey{chan_name, hostname}]) + this->send_user_join(hostname, chan_name, user, user_mode, self, resource); +} + +void Bridge::send_user_join(const std::string& hostname, const std::string& chan_name, + const IrcUser* user, const char user_mode, + const bool self, const std::string& resource) { std::string affiliation; std::string role; std::tie(role, affiliation) = get_role_affiliation_from_irc_mode(user_mode); - for (const auto& resource: this->resources_in_chan[ChannelKey{chan_name, hostname}]) - { - this->xmpp.send_user_join(chan_name + utils::empty_if_fixed_server("%" + hostname), user->nick, user->host, - affiliation, role, this->user_jid + "/" + resource, self); - } + this->xmpp.send_user_join(chan_name + utils::empty_if_fixed_server("%" + hostname), user->nick, user->host, + affiliation, role, this->user_jid + "/" + resource, self); } void Bridge::send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who) { - const auto encoding = in_encoding_for(*this, {chan_name + '%' + hostname}); for (const auto& resource: this->resources_in_chan[ChannelKey{chan_name, hostname}]) { - this->xmpp.send_topic(chan_name + utils::empty_if_fixed_server( - "%" + hostname), this->make_xmpp_body(topic, encoding), this->user_jid + "/" + resource, who); + this->send_topic(hostname, chan_name, topic, who, resource); } } +void Bridge::send_topic(const std::string& hostname, const std::string& chan_name, + const std::string& topic, const std::string& who, + const std::string& resource) +{ + const auto encoding = in_encoding_for(*this, {chan_name + '%' + hostname}); + this->xmpp.send_topic(chan_name + utils::empty_if_fixed_server( + "%" + hostname), this->make_xmpp_body(topic, encoding), this->user_jid + "/" + resource, who); + +} + std::string Bridge::get_own_nick(const Iid& iid) { IrcClient* irc = this->find_irc_client(iid.get_server()); @@ -682,13 +717,13 @@ size_t Bridge::active_clients() const void Bridge::kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author) { - for (const auto& resource: this->resources_in_chan[ChannelKey{iid.get_local(), iid.get_server()}]) + for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) this->xmpp.kick_user(std::to_string(iid), target, reason, author, this->user_jid + "/" + resource); } void Bridge::send_nickname_conflict_error(const Iid& iid, const std::string& nickname) { - for (const auto& resource: this->resources_in_chan[ChannelKey{iid.get_local(), iid.get_server()}]) + for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) this->xmpp.send_presence_error(std::to_string(iid), nickname, this->user_jid + "/" + resource, "cancel", "conflict", "409", ""); } @@ -698,7 +733,7 @@ void Bridge::send_affiliation_role_change(const Iid& iid, const std::string& tar std::string affiliation; std::tie(role, affiliation) = get_role_affiliation_from_irc_mode(mode); - for (const auto& resource: this->resources_in_chan[ChannelKey{iid.get_local(), iid.get_server()}]) + for (const auto& resource: this->resources_in_chan[iid.to_tuple()]) this->xmpp.send_affiliation_role_change(std::to_string(iid), target, affiliation, role, this->user_jid + "/" + resource); } @@ -812,13 +847,33 @@ bool Bridge::is_resource_in_server(const Bridge::IrcHostname& irc_hostname, cons return false; } +std::size_t Bridge::number_of_resources_in_chan(const Bridge::ChannelKey& channel_key) const +{ + auto it = this->resources_in_chan.find(channel_key); + if (it == this->resources_in_chan.end()) + return 0; + return it->second.size(); +} + void Bridge::generate_channel_join_for_resource(const Iid& iid, const std::string& resource) { IrcClient* irc = this->get_irc_client(iid.get_server()); IrcChannel* channel = irc->get_channel(iid.get_local()); + const auto self = channel->get_self(); + // Send the occupant list for (const auto& user: channel->get_users()) { - + if (user->nick != self->nick) + { + log_debug(user->nick); + this->send_user_join(iid.get_server(), iid.get_local(), + user.get(), user->get_most_significant_mode(irc->get_sorted_user_modes()), + false, resource); + } } + this->send_user_join(iid.get_server(), iid.get_local(), + self, self->get_most_significant_mode(irc->get_sorted_user_modes()), + true, resource); + this->send_topic(iid.get_server(), iid.get_local(), channel->topic, channel->topic_author, resource); } diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index e614779..eabd9af 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -70,7 +70,7 @@ public: void send_channel_message(const Iid& iid, const std::string& body); void send_private_message(const Iid& iid, const std::string& body, const std::string& type="PRIVMSG"); void send_raw_message(const std::string& hostname, const std::string& body); - void leave_irc_channel(Iid&& iid, std::string&& status_message); + void leave_irc_channel(Iid&& iid, std::string&& status_message, const std::string& resource); 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, const std::string& iq_id, const std::string& to_jid); @@ -119,15 +119,18 @@ public: /** * Send the presence of a new user in the MUC. */ - void send_user_join(const std::string& hostname, - const std::string& chan_name, - const IrcUser* user, - const char user_mode, + void send_user_join(const std::string& hostname, const std::string& chan_name, + const IrcUser* user, const char user_mode, + const bool self, const std::string& resource); + void send_user_join(const std::string& hostname, const std::string& chan_name, + const IrcUser* user, const char user_mode, const bool self); + /** * Send the topic of the MUC to the user */ void send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who); + void send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who, const std::string& resource); /** * Send a MUC message from some participant */ @@ -139,7 +142,7 @@ public: /** * Send an unavailable presence from this participant */ - void send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self); + void send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self, const std::string& resource=""); /** * Send presences to indicate that an user old_nick (ourself if self == * true) changed his nick to new_nick. The user_mode is needed because @@ -265,6 +268,7 @@ private: void add_resource_to_chan(const ChannelKey& channel_key, const std::string& resource); void remove_resource_from_chan(const ChannelKey& channel_key, const std::string& resource); bool is_resource_in_chan(const ChannelKey& channel_key, const std::string& resource) const; + std::size_t number_of_resources_in_chan(const ChannelKey& channel_key) const; void add_resource_to_server(const IrcHostname& irc_hostname, const std::string& resource); void remove_resource_from_server(const IrcHostname& irc_hostname, const std::string& resource); diff --git a/src/irc/iid.cpp b/src/irc/iid.cpp index 212fb8f..66b66b7 100644 --- a/src/irc/iid.cpp +++ b/src/irc/iid.cpp @@ -100,3 +100,8 @@ namespace std { } } } + +std::tuple Iid::to_tuple() const +{ + return std::make_tuple(this->get_local(), this->get_server()); +} diff --git a/src/irc/iid.hpp b/src/irc/iid.hpp index 1c026e8..9747595 100644 --- a/src/irc/iid.hpp +++ b/src/irc/iid.hpp @@ -60,6 +60,8 @@ public: std::string get_sep() const; + std::tuple to_tuple() const; + private: void init(const std::string& iid); diff --git a/src/irc/irc_channel.cpp b/src/irc/irc_channel.cpp index b1b3983..9801513 100644 --- a/src/irc/irc_channel.cpp +++ b/src/irc/irc_channel.cpp @@ -1,4 +1,5 @@ #include +#include IrcChannel::IrcChannel(): joined(false), @@ -36,15 +37,11 @@ IrcUser* IrcChannel::find_user(const std::string& name) const void IrcChannel::remove_user(const IrcUser* user) { - for (auto it = this->users.begin(); it != this->users.end(); ++it) - { - IrcUser* u = it->get(); - if (u->nick == user->nick) - { - this->users.erase(it); - break ; - } - } + this->users.erase(std::remove_if(this->users.begin(), this->users.end(), + [user](const std::unique_ptr& u) + { + return user->nick == u->nick; + }), this->users.end()); } void IrcChannel::remove_all_users() diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index ae68528..d16ffc7 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -618,9 +618,7 @@ void IrcClient::set_and_forward_user_list(const IrcMessage& message) const IrcUser* user = channel->add_user(nick, this->prefix_to_mode); if (user->nick != channel->get_self()->nick) { - this->bridge.send_user_join(this->hostname, chan_name, user, - user->get_most_significant_mode(this->sorted_user_modes), - false); + this->bridge.send_user_join(this->hostname, chan_name, user, user->get_most_significant_mode(this->sorted_user_modes), false); } else { @@ -644,9 +642,7 @@ void IrcClient::on_channel_join(const IrcMessage& message) else { const IrcUser* user = channel->add_user(nick, this->prefix_to_mode); - this->bridge.send_user_join(this->hostname, chan_name, user, - user->get_most_significant_mode(this->sorted_user_modes), - false); + this->bridge.send_user_join(this->hostname, chan_name, user, user->get_most_significant_mode(this->sorted_user_modes), false); } } @@ -746,9 +742,7 @@ void IrcClient::on_channel_completely_joined(const IrcMessage& message) const std::string chan_name = utils::tolower(message.arguments[1]); IrcChannel* channel = this->get_channel(chan_name); channel->joined = true; - this->bridge.send_user_join(this->hostname, chan_name, channel->get_self(), - channel->get_self()->get_most_significant_mode(this->sorted_user_modes), - true); + this->bridge.send_user_join(this->hostname, chan_name, channel->get_self(), channel->get_self()->get_most_significant_mode(this->sorted_user_modes), true); this->bridge.send_topic(this->hostname, chan_name, channel->topic, channel->topic_author); } diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index 7af097c..f075ce6 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -276,6 +276,8 @@ public: const Resolver& get_resolver() const { return this->dns_resolver; } + const std::vector& get_sorted_user_modes() const { return sorted_user_modes; } + private: /** * The hostname of the server we are connected to. diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 6a9bc87..62e17d0 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -158,7 +158,7 @@ void BiboumiComponent::handle_presence(const Stanza& stanza) else if (type == "unavailable") { const XmlNode* status = stanza.get_child("status", COMPONENT_NS); - bridge->leave_irc_channel(std::move(iid), status ? status->get_inner() : ""); + bridge->leave_irc_channel(std::move(iid), status ? status->get_inner() : "", from.resource); } } else -- cgit v1.2.3