From b11126a19dbaadf4c32fb8dbec22754ad0712c26 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sun, 8 Dec 2013 20:14:12 +0100 Subject: Provide a JID for IRC users, and add a stringprep dependency for this --- CMakeLists.txt | 11 ++++++++++ cmake/Modules/FindLibidn.cmake | 36 +++++++++++++++++++++++++++++++++ config.h.cmake | 1 + src/bridge/bridge.cpp | 6 ++++-- src/bridge/bridge.hpp | 5 ++++- src/irc/irc_client.cpp | 8 ++++---- src/irc/irc_message.cpp | 2 +- src/test.cpp | 5 +++++ src/xmpp/jid.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++ src/xmpp/jid.hpp | 10 +++++++++ src/xmpp/xmpp_component.cpp | 13 ++++++++++-- src/xmpp/xmpp_component.hpp | 5 ++++- 12 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 cmake/Modules/FindLibidn.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a8ae9c..ac93ff1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ find_package(Cryptopp REQUIRED) find_package(Iconv REQUIRED) include(FindEXPAT) find_package(EXPAT REQUIRED) +find_package(Libidn) include_directories("src/") include_directories(${EXPAT_INCLUDE_DIRS}) @@ -28,6 +29,12 @@ include_directories(${ICONV_INCLUDE_DIRS}) # coming from these headers. include_directories(SYSTEM ${CRYPTO++_INCLUDE_DIRS}) +if(LIBIDN_FOUND) + include_directories(${LIBIDN_INCLUDE_DIRS}) +else() + message("Building without stringprep support.") +endif() + set(POLLER "POLL" CACHE STRING "Choose the poller between POLL and EPOLL (Linux-only)") if((NOT ${POLLER} MATCHES "POLL") AND @@ -35,6 +42,7 @@ if((NOT ${POLLER} MATCHES "POLL") AND message(FATAL_ERROR "POLLER must be either POLL or EPOLL") endif() + # ## utils # @@ -83,6 +91,9 @@ file(GLOB source_xmpp add_library(xmpp STATIC ${source_xmpp}) target_link_libraries(xmpp bridge network utils logger ${CRYPTO++_LIBRARIES} ${EXPAT_LIBRARIES} pthread) +if(LIBIDN_FOUND) + target_link_libraries(xmpp ${LIBIDN_LIBRARIES}) +endif() # ## bridge diff --git a/cmake/Modules/FindLibidn.cmake b/cmake/Modules/FindLibidn.cmake new file mode 100644 index 0000000..4c0b2c5 --- /dev/null +++ b/cmake/Modules/FindLibidn.cmake @@ -0,0 +1,36 @@ +# - Find libidn +# Find the libidn library, and more particularly the stringprep header. +# +# This module defines the following variables: +# LIBIDN_FOUND - True if library and include directory are found +# If set to TRUE, the following are also defined: +# LIBIDN_INCLUDE_DIRS - The directory where to find the header file +# LIBIDN_LIBRARIES - Where to find the library file +# +# For conveniance, these variables are also set. They have the same values +# than the variables above. The user can thus choose his/her prefered way +# to way to write them. +# LIBIDN_INCLUDE_DIR +# LIBIDN_LIBRARY +# +# This file is in the public domain + +find_path(LIBIDN_INCLUDE_DIRS NAMES stringprep.h + DOC "The libidn include directory") + +# The library containing the stringprep module is libidn +find_library(LIBIDN_LIBRARIES NAMES idn + DOC "The libidn library") + +# Use some standard module to handle the QUIETLY and REQUIRED arguments, and +# set LIBIDN_FOUND to TRUE if these two variables are set. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libidn REQUIRED_VARS LIBIDN_LIBRARIES LIBIDN_INCLUDE_DIRS) + +# Compatibility for all the ways of writing these variables +if(LIBIDN_FOUND) + set(LIBIDN_INCLUDE_DIR ${LIBIDN_INCLUDE_DIRS}) + set(LIBIDN_LIBRARY ${LIBIDN_LIBRARIES}) +endif() + +mark_as_advanced(LIBIDN_INCLUDE_DIRS LIBIDN_LIBRARIES) \ No newline at end of file diff --git a/config.h.cmake b/config.h.cmake index e17cc80..8ee0fd3 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -1,2 +1,3 @@ #cmakedefine ICONV_SECOND_ARGUMENT_IS_CONST +#cmakedefine LIBIDN_FOUND #cmakedefine POLLER ${POLLER} diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 2ad6af8..973e095 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -167,9 +167,11 @@ void Bridge::send_xmpp_message(const std::string& from, const std::string& autho this->xmpp->send_message(from, this->make_xmpp_body(body), this->user_jid); } -void Bridge::send_user_join(const std::string& hostname, const std::string& chan_name, const std::string nick) +void Bridge::send_user_join(const std::string& hostname, + const std::string& chan_name, + const IrcUser* user) { - this->xmpp->send_user_join(chan_name + "%" + hostname, nick, this->user_jid); + this->xmpp->send_user_join(chan_name + "%" + hostname, user->nick, user->host, this->user_jid); } void Bridge::send_self_join(const std::string& hostname, const std::string& chan_name, const std::string nick) diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index 1443191..bbbca95 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -56,7 +57,9 @@ 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 std::string nick); + void send_user_join(const std::string& hostname, + const std::string& chan_name, + const IrcUser* user); /** * Send the self presence of an user when the MUC is fully joined. */ diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 0d4d102..0f29a2b 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -193,11 +193,11 @@ void IrcClient::set_and_forward_user_list(const IrcMessage& message) std::vector nicks = utils::split(message.arguments[3], ' '); for (const std::string& nick: nicks) { - IrcUser* user = channel->add_user(nick); + const IrcUser* user = channel->add_user(nick); if (user->nick != channel->get_self()->nick) { log_debug("Adding user [" << nick << "] to chan " << chan_name); - this->bridge->send_user_join(this->hostname, chan_name, user->nick); + this->bridge->send_user_join(this->hostname, chan_name, user); } } } @@ -214,8 +214,8 @@ void IrcClient::on_channel_join(const IrcMessage& message) } else { - IrcUser* user = channel->add_user(nick); - this->bridge->send_user_join(this->hostname, chan_name, user->nick); + const IrcUser* user = channel->add_user(nick); + this->bridge->send_user_join(this->hostname, chan_name, user); } } diff --git a/src/irc/irc_message.cpp b/src/irc/irc_message.cpp index 18fa3ec..eb3eb47 100644 --- a/src/irc/irc_message.cpp +++ b/src/irc/irc_message.cpp @@ -9,7 +9,7 @@ IrcMessage::IrcMessage(std::string&& line) if (line[0] == ':') { pos = line.find(" "); - this->prefix = line.substr(1, pos); + this->prefix = line.substr(1, pos - 1); line = line.substr(pos + 1, std::string::npos); } // command diff --git a/src/test.cpp b/src/test.cpp index ac1a85d..a8c0276 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -148,6 +148,11 @@ int main() assert(jid2.domain == "ツ.coucou"); assert(jid2.resource == "coucou@coucou/coucou"); + // Nodeprep + const std::string& badjid("~louiz@EpiK-7D9D1FDE.poez.io"); + const std::string correctjid = jidprep(badjid); + assert(correctjid == "~louiz@epik-7d9d1fde.poez.io"); + /** * Config */ diff --git a/src/xmpp/jid.cpp b/src/xmpp/jid.cpp index 29a5302..4f9917b 100644 --- a/src/xmpp/jid.cpp +++ b/src/xmpp/jid.cpp @@ -1,4 +1,12 @@ #include +#include +#include + +#ifdef LIBIDN_FOUND + #include +#endif + +#include Jid::Jid(const std::string& jid) { @@ -19,3 +27,41 @@ Jid::Jid(const std::string& jid) this->domain = jid.substr(at, slash - at); } + +#include + +static constexpr size_t max_jid_part_len = 1023; + +std::string jidprep(const std::string& original) +{ +#ifdef LIBIDN_FOUND + // TODO: cache the result + const std::string error_msg("Failed to convert " + original + " into a valid JID:"); + Jid jid(original); + + char local[max_jid_part_len] = {}; + memcpy(local, jid.local.data(), jid.local.size()); + Stringprep_rc rc = static_cast(::stringprep(local, max_jid_part_len, + static_cast(0), stringprep_xmpp_nodeprep)); + if (rc != STRINGPREP_OK) + { + log_error(error_msg + stringprep_strerror(rc)); + return ""; + } + + char domain[max_jid_part_len] = {}; + memcpy(domain, jid.domain.data(), jid.domain.size()); + rc = static_cast(::stringprep(domain, max_jid_part_len, + static_cast(0), stringprep_nameprep)); + if (rc != STRINGPREP_OK) + { + log_error(error_msg + stringprep_strerror(rc)); + return ""; + } + + return std::string(local) + "@" + domain; +#else + (void)original; + return ""; +#endif +} diff --git a/src/xmpp/jid.hpp b/src/xmpp/jid.hpp index 3027497..b6975a2 100644 --- a/src/xmpp/jid.hpp +++ b/src/xmpp/jid.hpp @@ -22,5 +22,15 @@ private: Jid& operator=(Jid&&) = delete; }; +/** + * Prepare the given UTF-8 string according to the XMPP node stringprep + * identifier profile. This is used to send properly-formed JID to the XMPP + * server. + * + * If the stringprep library is not found, we return an empty string. When + * this function is used, the result must always be checked for an empty + * value, and if this is the case it must not be used as a JID. + */ +std::string jidprep(const std::string& original); #endif // JID_INCLUDED diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 5e65595..6424d74 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -267,7 +267,10 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con this->send_stanza(node); } -void XmppComponent::send_user_join(const std::string& from, const std::string& nick, const std::string& to) +void XmppComponent::send_user_join(const std::string& from, + const std::string& nick, + const std::string& realjid, + const std::string& to) { XmlNode node("presence"); node["to"] = to; @@ -280,6 +283,12 @@ void XmppComponent::send_user_join(const std::string& from, const std::string& n XmlNode item("item"); item["affiliation"] = "member"; item["role"] = "participant"; + if (!realjid.empty()) + { + const std::string preped_jid = jidprep(realjid); + if (!preped_jid.empty()) + item["jid"] = preped_jid; + } item.close(); x.add_child(std::move(item)); x.close(); @@ -408,7 +417,7 @@ void XmppComponent::send_nick_change(const std::string& muc_name, const std::str if (self) this->send_self_join(muc_name, new_nick, jid_to); else - this->send_user_join(muc_name, new_nick, jid_to); + this->send_user_join(muc_name, new_nick, "", jid_to); } void XmppComponent::kick_user(const std::string& muc_name, diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index bf85536..15f8f2f 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -62,7 +62,10 @@ public: /** * Send a join from a new participant */ - void send_user_join(const std::string& from, const std::string& nick, const std::string& to); + void send_user_join(const std::string& from, + const std::string& nick, + const std::string& realjid, + const std::string& to); /** * Send the self join to the user */ -- cgit v1.2.3