From d8da7984b23bf8f30b5f8f53895cbae5be50347a Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 12 Nov 2014 06:16:10 +0100 Subject: Implement PING, user to user only (XMPP and IRC side, using CTCP PING) ref #2757 --- src/bridge/bridge.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++ src/bridge/bridge.hpp | 13 ++++++++++- src/irc/irc_client.cpp | 3 +++ src/xmpp/xmpp_component.cpp | 40 +++++++++++++++++++++++++++++++++ src/xmpp/xmpp_component.hpp | 8 +++++++ 5 files changed, 118 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index bc89073..08fd569 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -280,6 +282,50 @@ void Bridge::send_xmpp_version_to_irc(const Iid& iid, const std::string& name, c this->send_private_message(iid, "\01VERSION "s + result + "\01", "NOTICE"); } +void Bridge::send_irc_ping_result(const Iid& iid, const std::string& id) +{ + this->send_private_message(iid, "\01PING "s + utils::revstr(id), "NOTICE"); +} + +void Bridge::send_irc_user_ping_request(const std::string& irc_hostname, const std::string& nick, + const std::string& iq_id, const std::string& to_jid, + const std::string& from_jid) +{ + Iid iid(nick + "!" + irc_hostname); + this->send_private_message(iid, "\01PING " + iq_id + "\01"); + + irc_responder_callback_t cb = [this, nick, iq_id, to_jid, irc_hostname, from_jid](const std::string& hostname, const IrcMessage& message) -> bool + { + if (irc_hostname != hostname) + return false; + IrcUser user(message.prefix); + const std::string body = message.arguments[1]; + if (message.command == "NOTICE" && user.nick == nick && + message.arguments.size() >= 2 && body.substr(0, 6) == "\01PING ") + { + const std::string id = body.substr(6, body.size() - 6); + if (id != iq_id) + return false; + Jid jid(from_jid); + this->xmpp->send_iq_result(iq_id, to_jid, jid.local); + return true; + } + if (message.command == "401" && message.arguments.size() >= 2 + && message.arguments[1] == nick) + { + std::string error_message = "No such nick"; + if (message.arguments.size() >= 3) + error_message = message.arguments[2]; + this->xmpp->send_stanza_error("iq", to_jid, from_jid, iq_id, "cancel", "service-unavailable", + error_message, true); + return true; + } + + return false; + }; + this->add_waiting_irc(std::move(cb)); +} + void Bridge::send_irc_version_request(const std::string& irc_hostname, const std::string& target, const std::string& iq_id, const std::string& to_jid, const std::string& from_jid) @@ -437,6 +483,15 @@ void Bridge::send_iq_version_request(const std::string& nick, const std::string& this->xmpp->send_iq_version_request(nick + "!" + hostname, this->user_jid); } +void Bridge::send_xmpp_ping_request(const std::string& nick, const std::string& hostname, + const std::string& id) +{ + // Use revstr because the forwarded ping to target XMPP user must not be + // the same that the request iq, but we also need to get it back easily + // (revstr again) + this->xmpp->send_ping_request(nick + "!" + hostname, this->user_jid, utils::revstr(id)); +} + void Bridge::set_preferred_from_jid(const std::string& nick, const std::string& full_jid) { auto it = this->preferred_user_from.find(nick); diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index 1f45b27..f7edfaa 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -68,9 +68,16 @@ public: void set_channel_topic(const Iid& iid, const std::string& subject); void send_xmpp_version_to_irc(const Iid& iid, const std::string& name, const std::string& version, const std::string& os); + void send_irc_ping_result(const Iid& iid, const std::string& id); void send_irc_version_request(const std::string& irc_hostname, const std::string& target, const std::string& iq_id, const std::string& to_jid, const std::string& from_jid); + /** + * Directly send a CTCP PING request to the IRC user + */ + void send_irc_user_ping_request(const std::string& irc_hostname, const std::string& nick, + const std::string& iq_id, const std::string& to_jid, + const std::string& from_jid); /*** ** @@ -128,7 +135,11 @@ public: * Send an iq version request coming from nick!hostname@ */ void send_iq_version_request(const std::string& nick, const std::string& hostname); - + /** + * Send an iq ping request coming from nick!hostname@ + */ + void send_xmpp_ping_request(const std::string& nick, const std::string& hostname, + const std::string& id); /** * Misc */ diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 90f0644..707593f 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -409,6 +409,9 @@ void IrcClient::on_channel_message(const IrcMessage& message) "/me"s + body.substr(7, body.size() - 8), muc); else if (body.substr(1, 8) == "VERSION\01") this->bridge->send_iq_version_request(nick, this->hostname); + else if (body.substr(1, 5) == "PING ") + this->bridge->send_xmpp_ping_request(nick, this->hostname, + body.substr(6, body.size() - 7)); } else this->bridge->send_message(iid, nick, body, muc); diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 948e680..5f76bd3 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -555,6 +555,16 @@ void XmppComponent::handle_iq(const Stanza& stanza) stanza_error.disable(); } } + else if ((query = stanza.get_child("ping", PING_NS))) + { + Iid iid(to.local); + if (iid.is_user) + { // Ping any user (no check on the nick done ourself) + bridge->send_irc_user_ping_request(iid.get_server(), + iid.get_local(), id, from, to_str); + } + stanza_error.disable(); + } } else if (type == "result") { @@ -1080,6 +1090,36 @@ void XmppComponent::send_iq_version_request(const std::string& from, this->send_stanza(iq); } +void XmppComponent::send_ping_request(const std::string& from, + const std::string& jid_to, + const std::string& id) +{ + Stanza iq("iq"); + iq["type"] = "get"; + iq["id"] = id; + iq["from"] = from + "@" + this->served_hostname; + iq["to"] = jid_to; + XmlNode ping("ping"); + ping["xmlns"] = PING_NS; + ping.close(); + iq.add_child(std::move(ping)); + iq.close(); + this->send_stanza(iq); + + auto result_cb = [from, id](Bridge* bridge, const Stanza& stanza) + { + Jid to(stanza.get_tag("to")); + if (to.local != from) + { + log_error("Received a corresponding ping result, but the 'to' from " + "the response mismatches the 'from' of the request"); + } + else + bridge->send_irc_ping_result(from, id); + }; + this->waiting_iq[id] = result_cb; +} + void XmppComponent::send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from_local_part) { Stanza iq("iq"); diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 6ccc753..d7f7f7a 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -24,6 +24,8 @@ #define STREAMS_NS "urn:ietf:params:xml:ns:xmpp-streams" #define VERSION_NS "jabber:iq:version" #define ADHOC_NS "http://jabber.org/protocol/commands" +#define PING_NS "urn:xmpp:ping" + /** * A callback called when the waited iq result is received (it is matched * against the iq id) @@ -216,6 +218,12 @@ public: */ void send_iq_version_request(const std::string& from, const std::string& jid_to); + /** + * Send a ping request + */ + void send_ping_request(const std::string& from, + const std::string& jid_to, + const std::string& id); /** * Send an empty iq of type result */ -- cgit v1.2.3