diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 137 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.hpp | 80 |
2 files changed, 217 insertions, 0 deletions
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 + |