~singpolyma/biboumi

b11126a19dbaadf4c32fb8dbec22754ad0712c26 — Florent Le Coz 9 years ago cb718de
Provide a JID for IRC users, and add a stringprep dependency for this
M CMakeLists.txt => CMakeLists.txt +11 -0
@@ 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

A cmake/Modules/FindLibidn.cmake => cmake/Modules/FindLibidn.cmake +36 -0
@@ 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

M config.h.cmake => config.h.cmake +1 -0
@@ 1,2 1,3 @@
#cmakedefine ICONV_SECOND_ARGUMENT_IS_CONST
#cmakedefine LIBIDN_FOUND
#cmakedefine POLLER ${POLLER}

M src/bridge/bridge.cpp => src/bridge/bridge.cpp +4 -2
@@ 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)

M src/bridge/bridge.hpp => src/bridge/bridge.hpp +4 -1
@@ 3,6 3,7 @@

#include <irc/irc_client.hpp>
#include <bridge/colors.hpp>
#include <irc/irc_user.hpp>
#include <irc/iid.hpp>

#include <unordered_map>


@@ 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.
   */

M src/irc/irc_client.cpp => src/irc/irc_client.cpp +4 -4
@@ 193,11 193,11 @@ void IrcClient::set_and_forward_user_list(const IrcMessage& message)
  std::vector<std::string> 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);
    }
}


M src/irc/irc_message.cpp => src/irc/irc_message.cpp +1 -1
@@ 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

M src/test.cpp => src/test.cpp +5 -0
@@ 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
   */

M src/xmpp/jid.cpp => src/xmpp/jid.cpp +46 -0
@@ 1,4 1,12 @@
#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)
{


@@ 19,3 27,41 @@ Jid::Jid(const std::string& jid)

  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
}

M src/xmpp/jid.hpp => src/xmpp/jid.hpp +10 -0
@@ 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

M src/xmpp/xmpp_component.cpp => src/xmpp/xmpp_component.cpp +11 -2
@@ 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,

M src/xmpp/xmpp_component.hpp => src/xmpp/xmpp_component.hpp +4 -1
@@ 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
   */