summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorent Le Coz <louiz@louiz.org>2013-11-03 17:51:32 +0100
committerFlorent Le Coz <louiz@louiz.org>2013-11-03 17:51:32 +0100
commitf2f94618fcf87b4fc1ad86902c63a7a48be745b8 (patch)
treebe3e64ef9119f91b043b5c2cea96f7508158d1f9
parent5bbd34a3a909fa904ee4402f01dac6bac59211b1 (diff)
downloadbiboumi-f2f94618fcf87b4fc1ad86902c63a7a48be745b8.tar.gz
biboumi-f2f94618fcf87b4fc1ad86902c63a7a48be745b8.tar.bz2
biboumi-f2f94618fcf87b4fc1ad86902c63a7a48be745b8.tar.xz
biboumi-f2f94618fcf87b4fc1ad86902c63a7a48be745b8.zip
Add a basic XMPP component implementation, doing the authentication
-rw-r--r--CMakeLists.txt9
-rw-r--r--src/xmpp/xmpp_component.cpp137
-rw-r--r--src/xmpp/xmpp_component.hpp80
3 files changed, 226 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1c21b84..363eb79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,16 @@ set(${PROJECT_NAME}_VERSION_MINOR 1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pedantic -Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -fsanitize=address")
+#
+## Look for external libraries
+#
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
+find_package(Cryptopp REQUIRED)
+
include_directories("src/")
+# the SYSTEM flag tells the compiler that we don't care about warnings
+# coming from these headers.
+include_directories(SYSTEM ${CRYPTO++_INCLUDE_DIR})
#
## network
diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp
new file mode 100644
index 0000000..0563aa7
--- /dev/null
+++ b/src/xmpp/xmpp_component.cpp
@@ -0,0 +1,137 @@
+#include <xmpp/xmpp_component.hpp>
+
+#include <iostream>
+
+// CryptoPP
+#include <filters.h>
+#include <hex.h>
+#include <sha.h>
+
+XmppComponent::XmppComponent(const std::string& hostname, const std::string& secret):
+ served_hostname(hostname),
+ secret(secret),
+ authenticated(false)
+{
+ this->parser.add_stream_open_callback(std::bind(&XmppComponent::on_remote_stream_open, this,
+ std::placeholders::_1));
+ this->parser.add_stanza_callback(std::bind(&XmppComponent::on_stanza, this,
+ std::placeholders::_1));
+ this->parser.add_stream_close_callback(std::bind(&XmppComponent::on_remote_stream_close, this,
+ std::placeholders::_1));
+ this->stanza_handlers.emplace("handshake",
+ std::bind(&XmppComponent::handle_handshake, this,std::placeholders::_1));
+}
+
+XmppComponent::~XmppComponent()
+{
+}
+
+void XmppComponent::start()
+{
+ this->connect(this->served_hostname, "5347");
+}
+
+void XmppComponent::send_stanza(const Stanza& stanza)
+{
+ std::cout << "====== Sending ========" << std::endl;
+ std::cout << stanza.to_string() << std::endl;
+ this->send_data(stanza.to_string());
+}
+
+void XmppComponent::on_connected()
+{
+ std::cout << "connected to XMPP server" << std::endl;
+ XmlNode node("stream:stream", nullptr);
+ node["xmlns"] = "jabber:component:accept";
+ node["xmlns:stream"] = "http://etherx.jabber.org/streams";
+ node["to"] = "irc.abricot";
+ this->send_stanza(node);
+
+}
+
+void XmppComponent::on_connection_close()
+{
+ std::cout << "XMPP server closed connection" << std::endl;
+}
+
+void XmppComponent::parse_in_buffer()
+{
+ this->parser.XML_Parse(this->in_buf.data(), this->in_buf.size(), false);
+ this->in_buf.clear();
+}
+
+void XmppComponent::on_remote_stream_open(const XmlNode& node)
+{
+ std::cout << "====== DOCUMENT_OPEN =======" << std::endl;
+ std::cout << node.to_string() << std::endl;
+ try
+ {
+ this->stream_id = node["id"];
+ }
+ catch (const AttributeNotFound& e)
+ {
+ std::cout << "Error: no attribute 'id' found" << std::endl;
+ this->send_stream_error("bad-format", "missing 'id' attribute");
+ this->close_document();
+ return ;
+ }
+
+ // Try to authenticate
+ CryptoPP::SHA1 sha1;
+ std::string digest;
+ CryptoPP::StringSource foo(this->stream_id + this->secret, true,
+ new CryptoPP::HashFilter(sha1,
+ new CryptoPP::HexEncoder(
+ new CryptoPP::StringSink(digest), false)));
+ Stanza handshake("handshake", nullptr);
+ handshake.set_inner(digest);
+ handshake.close();
+ this->send_stanza(handshake);
+}
+
+void XmppComponent::on_remote_stream_close(const XmlNode& node)
+{
+ std::cout << "====== DOCUMENT_CLOSE =======" << std::endl;
+ std::cout << node.to_string() << std::endl;
+}
+
+void XmppComponent::on_stanza(const Stanza& stanza)
+{
+ std::cout << "=========== STANZA ============" << std::endl;
+ std::cout << stanza.to_string() << std::endl;
+ try
+ {
+ const auto& handler = this->stanza_handlers.at(stanza.get_name());
+ handler(stanza);
+ }
+ catch (const std::out_of_range& exception)
+ {
+ std::cout << "No handler for stanza of type " << stanza.get_name() << std::endl;
+ return;
+ }
+}
+
+void XmppComponent::send_stream_error(const std::string& name, const std::string& explanation)
+{
+ XmlNode node("stream:error", nullptr);
+ XmlNode error(name, nullptr);
+ error["xmlns"] = "urn:ietf:params:xml:ns:xmpp-streams";
+ if (!explanation.empty())
+ error.set_inner(explanation);
+ error.close();
+ node.add_child(std::move(error));
+ node.close();
+ this->send_stanza(node);
+}
+
+void XmppComponent::close_document()
+{
+ std::cout << "====== Sending ========" << std::endl;
+ std::cout << "</stream:stream>" << std::endl;
+ this->send_data("</stream:stream>");
+}
+
+void XmppComponent::handle_handshake(const Stanza& stanza)
+{
+ this->authenticated = true;
+}
diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp
new file mode 100644
index 0000000..464ecaa
--- /dev/null
+++ b/src/xmpp/xmpp_component.hpp
@@ -0,0 +1,80 @@
+#ifndef XMPP_COMPONENT_INCLUDED
+# define XMPP_COMPONENT_INCLUDED
+
+#include <string>
+
+#include <network/socket_handler.hpp>
+
+#include <xmpp/xmpp_parser.hpp>
+
+#include <unordered_map>
+
+/**
+ * An XMPP component, communicating with an XMPP server using the protocole
+ * described in XEP-0114: Jabber Component Protocol
+ *
+ * TODO: implement XEP-0225: Component Connections
+ */
+class XmppComponent: public SocketHandler
+{
+public:
+ explicit XmppComponent(const std::string& hostname, const std::string& secret);
+ ~XmppComponent();
+ void on_connected();
+ void on_connection_close();
+ void parse_in_buffer();
+
+ /**
+ * Connect to the XMPP server
+ */
+ void start();
+ /**
+ * Serialize the stanza and add it to the out_buf to be sent to the
+ * server.
+ */
+ void send_stanza(const Stanza& stanza);
+ /**
+ * Handle the opening of the remote stream
+ */
+ void on_remote_stream_open(const XmlNode& node);
+ /**
+ * Handle the closing of the remote stream
+ */
+ void on_remote_stream_close(const XmlNode& node);
+ /**
+ * Handle received stanzas
+ */
+ void on_stanza(const Stanza& stanza);
+ /**
+ * Send an error stanza. Message being the name of the element inside the
+ * stanza, and explanation being a short human-readable sentence
+ * describing the error.
+ */
+ void send_stream_error(const std::string& message, const std::string& explanation);
+ /**
+ * Send the closing signal for our document (not closing the connection though).
+ */
+ void close_document();
+
+ /**
+ * Handle the various stanza types
+ */
+ void handle_handshake(const Stanza& stanza);
+
+private:
+ XmppParser parser;
+ std::string stream_id;
+ std::string served_hostname;
+ std::string secret;
+ bool authenticated;
+
+ std::unordered_map<std::string, std::function<void(const Stanza&)>> stanza_handlers;
+
+ XmppComponent(const XmppComponent&) = delete;
+ XmppComponent(XmppComponent&&) = delete;
+ XmppComponent& operator=(const XmppComponent&) = delete;
+ XmppComponent& operator=(XmppComponent&&) = delete;
+};
+
+#endif // XMPP_COMPONENT_INCLUDED
+