Commit b11126a1 authored by louiz’'s avatar louiz’

Provide a JID for IRC users, and add a stringprep dependency for this

parent cb718def
...@@ -20,6 +20,7 @@ find_package(Cryptopp REQUIRED) ...@@ -20,6 +20,7 @@ find_package(Cryptopp REQUIRED)
find_package(Iconv REQUIRED) find_package(Iconv REQUIRED)
include(FindEXPAT) include(FindEXPAT)
find_package(EXPAT REQUIRED) find_package(EXPAT REQUIRED)
find_package(Libidn)
include_directories("src/") include_directories("src/")
include_directories(${EXPAT_INCLUDE_DIRS}) include_directories(${EXPAT_INCLUDE_DIRS})
...@@ -28,6 +29,12 @@ include_directories(${ICONV_INCLUDE_DIRS}) ...@@ -28,6 +29,12 @@ include_directories(${ICONV_INCLUDE_DIRS})
# coming from these headers. # coming from these headers.
include_directories(SYSTEM ${CRYPTO++_INCLUDE_DIRS}) 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 set(POLLER "POLL" CACHE STRING
"Choose the poller between POLL and EPOLL (Linux-only)") "Choose the poller between POLL and EPOLL (Linux-only)")
if((NOT ${POLLER} MATCHES "POLL") AND if((NOT ${POLLER} MATCHES "POLL") AND
...@@ -35,6 +42,7 @@ 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") message(FATAL_ERROR "POLLER must be either POLL or EPOLL")
endif() endif()
# #
## utils ## utils
# #
...@@ -83,6 +91,9 @@ file(GLOB source_xmpp ...@@ -83,6 +91,9 @@ file(GLOB source_xmpp
add_library(xmpp STATIC ${source_xmpp}) add_library(xmpp STATIC ${source_xmpp})
target_link_libraries(xmpp bridge network utils logger target_link_libraries(xmpp bridge network utils logger
${CRYPTO++_LIBRARIES} ${EXPAT_LIBRARIES} pthread) ${CRYPTO++_LIBRARIES} ${EXPAT_LIBRARIES} pthread)
if(LIBIDN_FOUND)
target_link_libraries(xmpp ${LIBIDN_LIBRARIES})
endif()
# #
## bridge ## bridge
......
# - 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
#cmakedefine ICONV_SECOND_ARGUMENT_IS_CONST #cmakedefine ICONV_SECOND_ARGUMENT_IS_CONST
#cmakedefine LIBIDN_FOUND
#cmakedefine POLLER ${POLLER} #cmakedefine POLLER ${POLLER}
...@@ -167,9 +167,11 @@ void Bridge::send_xmpp_message(const std::string& from, const std::string& autho ...@@ -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); 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) void Bridge::send_self_join(const std::string& hostname, const std::string& chan_name, const std::string nick)
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <irc/irc_client.hpp> #include <irc/irc_client.hpp>
#include <bridge/colors.hpp> #include <bridge/colors.hpp>
#include <irc/irc_user.hpp>
#include <irc/iid.hpp> #include <irc/iid.hpp>
#include <unordered_map> #include <unordered_map>
...@@ -56,7 +57,9 @@ public: ...@@ -56,7 +57,9 @@ public:
/** /**
* Send the presence of a new user in the MUC. * 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. * Send the self presence of an user when the MUC is fully joined.
*/ */
......
...@@ -193,11 +193,11 @@ void IrcClient::set_and_forward_user_list(const IrcMessage& message) ...@@ -193,11 +193,11 @@ void IrcClient::set_and_forward_user_list(const IrcMessage& message)
std::vector<std::string> nicks = utils::split(message.arguments[3], ' '); std::vector<std::string> nicks = utils::split(message.arguments[3], ' ');
for (const std::string& nick: nicks) 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) if (user->nick != channel->get_self()->nick)
{ {
log_debug("Adding user [" << nick << "] to chan " << chan_name); 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) ...@@ -214,8 +214,8 @@ void IrcClient::on_channel_join(const IrcMessage& message)
} }
else else
{ {
IrcUser* user = channel->add_user(nick); const IrcUser* user = channel->add_user(nick);
this->bridge->send_user_join(this->hostname, chan_name, user->nick); this->bridge->send_user_join(this->hostname, chan_name, user);
} }
} }
......
...@@ -9,7 +9,7 @@ IrcMessage::IrcMessage(std::string&& line) ...@@ -9,7 +9,7 @@ IrcMessage::IrcMessage(std::string&& line)
if (line[0] == ':') if (line[0] == ':')
{ {
pos = line.find(" "); pos = line.find(" ");
this->prefix = line.substr(1, pos); this->prefix = line.substr(1, pos - 1);
line = line.substr(pos + 1, std::string::npos); line = line.substr(pos + 1, std::string::npos);
} }
// command // command
......
...@@ -148,6 +148,11 @@ int main() ...@@ -148,6 +148,11 @@ int main()
assert(jid2.domain == "ツ.coucou"); assert(jid2.domain == "ツ.coucou");
assert(jid2.resource == "coucou@coucou/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 * Config
*/ */
......
#include <xmpp/jid.hpp> #include <xmpp/jid.hpp>
#include <config.h>
#include <cstring>
#ifdef LIBIDN_FOUND
#include <stringprep.h>
#endif
#include <logger/logger.hpp>
Jid::Jid(const std::string& jid) Jid::Jid(const std::string& jid)
{ {
...@@ -19,3 +27,41 @@ Jid::Jid(const std::string& jid) ...@@ -19,3 +27,41 @@ Jid::Jid(const std::string& jid)
this->domain = jid.substr(at, slash - at); this->domain = jid.substr(at, slash - at);
} }
#include <iostream>
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_rc>(::stringprep(local, max_jid_part_len,
static_cast<Stringprep_profile_flags>(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_rc>(::stringprep(domain, max_jid_part_len,
static_cast<Stringprep_profile_flags>(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
}
...@@ -22,5 +22,15 @@ private: ...@@ -22,5 +22,15 @@ private:
Jid& operator=(Jid&&) = delete; 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 #endif // JID_INCLUDED
...@@ -267,7 +267,10 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con ...@@ -267,7 +267,10 @@ void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, con
this->send_stanza(node); 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"); XmlNode node("presence");
node["to"] = to; node["to"] = to;
...@@ -280,6 +283,12 @@ void XmppComponent::send_user_join(const std::string& from, const std::string& n ...@@ -280,6 +283,12 @@ void XmppComponent::send_user_join(const std::string& from, const std::string& n
XmlNode item("item"); XmlNode item("item");
item["affiliation"] = "member"; item["affiliation"] = "member";
item["role"] = "participant"; item["role"] = "participant";
if (!realjid.empty())
{
const std::string preped_jid = jidprep(realjid);
if (!preped_jid.empty())
item["jid"] = preped_jid;
}
item.close(); item.close();
x.add_child(std::move(item)); x.add_child(std::move(item));
x.close(); x.close();
...@@ -408,7 +417,7 @@ void XmppComponent::send_nick_change(const std::string& muc_name, const std::str ...@@ -408,7 +417,7 @@ void XmppComponent::send_nick_change(const std::string& muc_name, const std::str
if (self) if (self)
this->send_self_join(muc_name, new_nick, jid_to); this->send_self_join(muc_name, new_nick, jid_to);
else 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, void XmppComponent::kick_user(const std::string& muc_name,
......
...@@ -62,7 +62,10 @@ public: ...@@ -62,7 +62,10 @@ public:
/** /**
* Send a join from a new participant * 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 * Send the self join to the user
*/ */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment