From 7c671499350e22f8bfba2f72b9827aa5b200f7b0 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 9 Nov 2013 23:17:48 +0100 Subject: Implement part and join, both ways --- src/bridge/bridge.cpp | 12 ++++++++++ src/bridge/bridge.hpp | 6 +++++ src/irc/irc_channel.cpp | 24 ++++++++++++++++++++ src/irc/irc_channel.hpp | 2 ++ src/irc/irc_client.cpp | 54 +++++++++++++++++++++++++++++++++++++++------ src/irc/irc_client.hpp | 10 ++++++++- src/irc/irc_user.cpp | 8 +++++-- src/xmpp/xmpp_component.cpp | 38 +++++++++++++++++++++++++++++-- src/xmpp/xmpp_component.hpp | 4 ++++ src/xmpp/xmpp_stanza.cpp | 2 ++ 10 files changed, 148 insertions(+), 12 deletions(-) diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index f028f5b..9dbea2f 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -82,11 +82,23 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body) this->xmpp->send_muc_message(iid.chan + "%" + iid.server, irc->get_own_nick(), body, this->user_jid); } +void Bridge::leave_irc_channel(Iid&& iid, std::string&& status_message) +{ + IrcClient* irc = this->get_irc_client(iid.server); + if (irc) + irc->send_part_command(iid.chan, status_message); +} + void Bridge::send_muc_message(const Iid& iid, const std::string& nick, const std::string& body) { this->xmpp->send_muc_message(iid.chan + "%" + iid.server, nick, this->sanitize_for_xmpp(body), this->user_jid); } +void Bridge::send_muc_leave(Iid&& iid, std::string&& nick, 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_xmpp_message(const std::string& from, const std::string& author, const std::string& msg) { std::string body; diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index f7fd7c6..93bb321 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -32,6 +32,7 @@ public: void join_irc_channel(const Iid& iid, const std::string& username); void send_channel_message(const Iid& iid, const std::string& body); + void leave_irc_channel(Iid&& iid, std::string&& status_message); /*** ** @@ -60,6 +61,11 @@ public: * Send a MUC message from some participant */ void send_muc_message(const Iid& iid, const std::string& nick, const std::string& body); + /** + * Send an unavailable presence from this participant + */ + void send_muc_leave(Iid&& iid, std::string&& nick, std::string&& message, const bool self); + private: /** * Returns the client for the given hostname, create one (and use the diff --git a/src/irc/irc_channel.cpp b/src/irc/irc_channel.cpp index 223305b..6daf708 100644 --- a/src/irc/irc_channel.cpp +++ b/src/irc/irc_channel.cpp @@ -22,3 +22,27 @@ IrcUser* IrcChannel::get_self() const { return this->self.get(); } + +IrcUser* IrcChannel::find_user(const std::string& name) +{ + IrcUser user(name); + for (const auto& u: this->users) + { + if (u->nick == user.nick) + return u.get(); + } + return nullptr; +} + +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 ; + } + } +} diff --git a/src/irc/irc_channel.hpp b/src/irc/irc_channel.hpp index da0f298..3786697 100644 --- a/src/irc/irc_channel.hpp +++ b/src/irc/irc_channel.hpp @@ -20,6 +20,8 @@ public: void set_self(const std::string& name); IrcUser* get_self() const; IrcUser* add_user(const std::string& name); + IrcUser* find_user(const std::string& name); + void remove_user(const IrcUser* user); private: std::unique_ptr self; diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index cf57bd7..e3d7653 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -76,7 +76,7 @@ void IrcClient::parse_in_buffer() message.command == "372") this->forward_server_message(message); else if (message.command == "JOIN") - this->on_self_channel_join(message); + this->on_channel_join(message); else if (message.command == "PRIVMSG") this->on_channel_message(message); else if (message.command == "353") @@ -87,6 +87,8 @@ void IrcClient::parse_in_buffer() this->on_channel_completely_joined(message); else if (message.command == "001") this->on_welcome_message(message); + else if (message.command == "PART") + this->on_part(message); } } @@ -128,9 +130,7 @@ void IrcClient::send_join_command(const std::string& chan_name) this->channels_to_join.push_back(chan_name); return ; } - IrcChannel* channel = this->get_channel(chan_name); - if (channel->joined == false) - this->send_message(IrcMessage("JOIN", {chan_name})); + this->send_message(IrcMessage("JOIN", {chan_name})); } bool IrcClient::send_channel_message(const std::string& chan_name, const std::string& body) @@ -145,6 +145,15 @@ bool IrcClient::send_channel_message(const std::string& chan_name, const std::st return true; } +void IrcClient::send_part_command(const std::string& chan_name, const std::string& status_message) +{ + IrcChannel* channel = this->get_channel(chan_name); + if (channel->joined == true) + { + this->send_message(IrcMessage("PART", {chan_name, status_message})); + } +} + void IrcClient::send_pong_command(const IrcMessage& message) { const std::string id = message.arguments[0]; @@ -175,12 +184,21 @@ void IrcClient::set_and_forward_user_list(const IrcMessage& message) } } -void IrcClient::on_self_channel_join(const IrcMessage& message) +void IrcClient::on_channel_join(const IrcMessage& message) { const std::string chan_name = message.arguments[0]; IrcChannel* channel = this->get_channel(chan_name); - channel->joined = true; - channel->set_self(message.prefix); + const std::string nick = message.prefix; + if (channel->joined == false) + { + channel->joined = true; + channel->set_self(nick); + } + else + { + IrcUser* user = channel->add_user(nick); + this->bridge->send_user_join(this->hostname, chan_name, user->nick); + } } void IrcClient::on_channel_message(const IrcMessage& message) @@ -217,3 +235,25 @@ void IrcClient::on_welcome_message(const IrcMessage& message) this->send_join_command(chan_name); this->channels_to_join.clear(); } + +void IrcClient::on_part(const IrcMessage& message) +{ + const std::string chan_name = message.arguments[0]; + IrcChannel* channel = this->get_channel(chan_name); + std::string txt; + if (message.arguments.size() >= 2) + txt = message.arguments[1]; + const IrcUser* user = channel->find_user(message.prefix); + if (user) + { + std::string nick = user->nick; + channel->remove_user(user); + Iid iid; + iid.chan = chan_name; + iid.server = this->hostname; + bool self = channel->get_self()->nick == nick; + this->bridge->send_muc_leave(std::move(iid), std::move(nick), std::move(txt), self); + if (self) + channel->joined = false; + } +} diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index 50f3781..e58ffbc 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -75,6 +75,10 @@ public: * Return true if the message was actually sent */ bool send_channel_message(const std::string& chan_name, const std::string& body); + /** + * Send the PART irc command + */ + void send_part_command(const std::string& chan_name, const std::string& status_message); /** * Forward the server message received from IRC to the XMPP component */ @@ -88,7 +92,7 @@ public: * Remember our nick and host, when we are joined to the channel. The list * of user comes after so we do not send the self-presence over XMPP yet. */ - void on_self_channel_join(const IrcMessage& message); + void on_channel_join(const IrcMessage& message); /** * When a channel message is received */ @@ -106,6 +110,10 @@ public: * When a message 001 is received, join the rooms we wanted to join, and set our actual nickname */ void on_welcome_message(const IrcMessage& message); + /** + * When a PART message is received + */ + void on_part(const IrcMessage& message); private: /** diff --git a/src/irc/irc_user.cpp b/src/irc/irc_user.cpp index fc853bc..afe8623 100644 --- a/src/irc/irc_user.cpp +++ b/src/irc/irc_user.cpp @@ -7,14 +7,18 @@ IrcUser::IrcUser(const std::string& name) const std::string::size_type sep = name.find("!"); if (sep == std::string::npos) { - if (name[0] == '@' || name[0] == '+') + if (name[0] == '~' || name[0] == '&' + || name[0] == '@' || name[0] == '%' + || name[0] == '+') this->nick = name.substr(1); else this->nick = name; } else { - if (name[0] == '@' || name[0] == '+') + if (name[0] == '~' || name[0] == '&' + || name[0] == '@' || name[0] == '%' + || name[0] == '+') this->nick = name.substr(1, sep); else this->nick = name.substr(0, sep); diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index cd9cd6f..83091ba 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -149,8 +149,22 @@ void XmppComponent::handle_presence(const Stanza& stanza) Bridge* bridge = this->get_user_bridge(stanza["from"]); Jid to(stanza["to"]); Iid iid(to.local); - if (!iid.chan.empty() && !iid.server.empty()) - bridge->join_irc_channel(iid, to.resource); + std::string type; + try { + type = stanza["type"]; + } + catch (const AttributeNotFound&) {} + + 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); + else if (type == "unavailable") + { + XmlNode* status = stanza.get_child("status"); + bridge->leave_irc_channel(std::move(iid), status ? std::move(status->get_inner()) : ""); + } + } } void XmppComponent::handle_message(const Stanza& stanza) @@ -269,3 +283,23 @@ void XmppComponent::send_muc_message(const std::string& muc_name, const std::str message.close(); this->send_stanza(message); } + +void XmppComponent::send_muc_leave(std::string&& muc_name, std::string&& nick, std::string&& message, const std::string& jid_to, const bool self) +{ + Stanza presence("presence"); + presence["to"] = jid_to; + presence["from"] = muc_name + "@" + this->served_hostname + "/" + nick; + presence["type"] = "unavailable"; + if (!message.empty() || self) + { + XmlNode status("status"); + if (!message.empty()) + status.set_inner(std::move(message)); + if (self) + status["code"] = "110"; + status.close(); + presence.add_child(std::move(status)); + } + presence.close(); + this->send_stanza(presence); +} diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 73eadd2..a5127a9 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -75,6 +75,10 @@ public: * Send a (non-private) message to the MUC */ void send_muc_message(const std::string& muc_name, const std::string& nick, const std::string body_str, const std::string& jid_to); + /** + * 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); /** * Handle the various stanza types */ diff --git a/src/xmpp/xmpp_stanza.cpp b/src/xmpp/xmpp_stanza.cpp index d856836..ab26304 100644 --- a/src/xmpp/xmpp_stanza.cpp +++ b/src/xmpp/xmpp_stanza.cpp @@ -1,5 +1,7 @@ #include +#include + #include std::string xml_escape(const std::string& data) -- cgit v1.2.3