summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt15
-rw-r--r--src/bridge/bridge.cpp57
-rw-r--r--src/bridge/bridge.hpp94
-rw-r--r--src/irc/iid.cpp17
-rw-r--r--src/irc/iid.hpp34
-rw-r--r--src/irc/irc_channel.cpp24
-rw-r--r--src/irc/irc_channel.hpp34
-rw-r--r--src/irc/irc_client.cpp105
-rw-r--r--src/irc/irc_client.hpp60
-rw-r--r--src/irc/irc_user.cpp24
-rw-r--r--src/irc/irc_user.hpp24
-rw-r--r--src/main.cpp11
-rw-r--r--src/xmpp/jid.cpp19
-rw-r--r--src/xmpp/jid.hpp26
-rw-r--r--src/xmpp/stanza.hpp18
-rw-r--r--src/xmpp/xmpp_component.cpp110
-rw-r--r--src/xmpp/xmpp_component.hpp36
17 files changed, 694 insertions, 14 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 363eb79..bff724c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,7 +32,7 @@ add_library(network STATIC ${source_network})
file(GLOB source_irc
src/irc/*.[hc]pp)
add_library(irc STATIC ${source_irc})
-target_link_libraries(irc network ${CRYPTO++_LIBRARIES} expatpp)
+target_link_libraries(irc network)
#
## xmpplib
@@ -40,9 +40,18 @@ target_link_libraries(irc network ${CRYPTO++_LIBRARIES} expatpp)
file(GLOB source_xmpp
src/xmpp/*.[hc]pp)
add_library(xmpp STATIC ${source_xmpp})
-target_link_libraries(xmpp network )
+target_link_libraries(xmpp bridge network ${CRYPTO++_LIBRARIES} expatpp)
+
+#
+## bridge
+#
+file(GLOB source_bridge
+ src/bridge/*.[hc]pp)
+add_library(bridge STATIC ${source_bridge})
+target_link_libraries(bridge xmpp irc)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME}
xmpp
- irc) \ No newline at end of file
+ irc
+ bridge) \ No newline at end of file
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp
new file mode 100644
index 0000000..638777d
--- /dev/null
+++ b/src/bridge/bridge.cpp
@@ -0,0 +1,57 @@
+#include <bridge/bridge.hpp>
+#include <xmpp/xmpp_component.hpp>
+#include <network/poller.hpp>
+
+Bridge::Bridge(const std::string& user_jid, XmppComponent* xmpp, Poller* poller):
+ user_jid(user_jid),
+ xmpp(xmpp),
+ poller(poller)
+{
+}
+
+Bridge::~Bridge()
+{
+}
+
+IrcClient* Bridge::get_irc_client(const std::string& hostname, const std::string& username)
+{
+ try
+ {
+ return this->irc_clients.at(hostname).get();
+ }
+ catch (const std::out_of_range& exception)
+ {
+ this->irc_clients.emplace(hostname, std::make_shared<IrcClient>(hostname, username, this));
+ std::shared_ptr<IrcClient> irc = this->irc_clients.at(hostname);
+ this->poller->add_socket_handler(irc);
+ irc->start();
+ return irc.get();
+ }
+}
+
+void Bridge::join_irc_channel(const Iid& iid, const std::string& username)
+{
+ IrcClient* irc = this->get_irc_client(iid.server, username);
+ irc->send_join_command(iid.chan);
+}
+
+void Bridge::send_xmpp_message(const std::string& from, const std::string& author, const std::string& msg)
+{
+ const std::string body = std::string("[") + author + std::string("] ") + msg;
+ this->xmpp->send_message(from, body, this->user_jid);
+}
+
+void Bridge::send_user_join(const std::string& hostname, const std::string& chan_name, const std::string nick)
+{
+ this->xmpp->send_user_join(chan_name + "%" + hostname, nick, this->user_jid);
+}
+
+void Bridge::send_self_join(const std::string& hostname, const std::string& chan_name, const std::string nick)
+{
+ this->xmpp->send_self_join(chan_name + "%" + hostname, nick, this->user_jid);
+}
+
+void Bridge::send_topic(const std::string& hostname, const std::string& chan_name, const std::string topic)
+{
+ this->xmpp->send_topic(chan_name + "%" + hostname, topic, this->user_jid);
+}
diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp
new file mode 100644
index 0000000..f9ddcca
--- /dev/null
+++ b/src/bridge/bridge.hpp
@@ -0,0 +1,94 @@
+#ifndef BRIDGE_INCLUDED
+# define BRIDGE_INCLUDED
+
+#include <irc/irc_client.hpp>
+#include <irc/iid.hpp>
+
+#include <unordered_map>
+#include <string>
+#include <memory>
+
+class XmppComponent;
+class Poller;
+
+/**
+ * One bridge is spawned for each XMPP user that uses the component. The
+ * bridge spawns IrcClients when needed (when the user wants to join a
+ * channel on a new server) and does the translation between the two
+ * protocols.
+ */
+class Bridge
+{
+public:
+ explicit Bridge(const std::string& user_jid, XmppComponent* xmpp, Poller* poller);
+ ~Bridge();
+
+ /***
+ **
+ ** From XMPP to IRC.
+ **
+ **/
+
+ void join_irc_channel(const Iid& iid, const std::string& username);
+
+ /***
+ **
+ ** From IRC to XMPP.
+ **
+ **/
+
+ /**
+ * Send a message corresponding to a server NOTICE, the from attribute
+ * should be juste the server hostname@irc.component.
+ */
+ void send_xmpp_message(const std::string& from, const std::string& author, const std::string& msg);
+ /**
+ * 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);
+ /**
+ * Send the self presence of an user when the MUC is fully joined.
+ */
+ void send_self_join(const std::string& hostname, const std::string& chan_name, const std::string nick);
+ /**
+ * Send the topic of the MUC to the user
+ */
+ void send_topic(const std::string& hostname, const std::string& chan_name, const std::string topic);
+
+private:
+ /**
+ * Returns the client for the given hostname, create one (and use the
+ * username in this case) if none is found, and connect that newly-created
+ * client immediately.
+ */
+ IrcClient* get_irc_client(const std::string& hostname, const std::string& username);
+ /**
+ * The JID of the user associated with this bridge. Messages from/to this
+ * JID are only managed by this bridge.
+ */
+ std::string user_jid;
+ /**
+ * One IrcClient for each IRC server we need to be connected to.
+ * The pointer is shared by the bridge and the poller.
+ */
+ std::unordered_map<std::string, std::shared_ptr<IrcClient>> irc_clients;
+ /**
+ * A raw pointer, because we do not own it, the XMPP component owns us,
+ * but we still need to communicate with it, when sending messages from
+ * IRC to XMPP.
+ */
+ XmppComponent* xmpp;
+ /**
+ * Poller, to give it the IrcClients that we spawn, to make it manage
+ * their sockets.
+ * We don't own it.
+ */
+ Poller* poller;
+
+ Bridge(const Bridge&) = delete;
+ Bridge(Bridge&& other) = delete;
+ Bridge& operator=(const Bridge&) = delete;
+ Bridge& operator=(Bridge&&) = delete;
+};
+
+#endif // BRIDGE_INCLUDED
diff --git a/src/irc/iid.cpp b/src/irc/iid.cpp
new file mode 100644
index 0000000..ffc8d88
--- /dev/null
+++ b/src/irc/iid.cpp
@@ -0,0 +1,17 @@
+#include <irc/iid.hpp>
+
+Iid::Iid(const std::string& iid)
+{
+ std::string::size_type sep = iid.find("%");
+ if (sep != std::string::npos)
+ {
+ this->chan = iid.substr(0, sep);
+ sep++;
+ }
+ else
+ {
+ this->chan = iid;
+ return;
+ }
+ this->server = iid.substr(sep);
+}
diff --git a/src/irc/iid.hpp b/src/irc/iid.hpp
new file mode 100644
index 0000000..aacc9e6
--- /dev/null
+++ b/src/irc/iid.hpp
@@ -0,0 +1,34 @@
+#ifndef IID_INCLUDED
+# define IID_INCLUDED
+
+#include <string>
+
+/**
+ * A name representing an IRC channel, on the same model than the XMPP JIDs (but much simpler).
+ * The separator between the server and the channel name is '%'
+ * #test%irc.freenode.org has :
+ * - chan: "#test" (the # is part of the name, it could very well be absent, or & instead
+ * - server: "irc.freenode.org"
+ * #test has:
+ * - chan: "#test"
+ * - server: ""
+ * %irc.freenode.org:
+ * - chan: ""
+ * - server: "irc.freenode.org"
+ */
+class Iid
+{
+public:
+ explicit Iid(const std::string& iid);
+
+ std::string chan;
+ std::string server;
+
+private:
+ Iid(const Iid&) = delete;
+ Iid(Iid&&) = delete;
+ Iid& operator=(const Iid&) = delete;
+ Iid& operator=(Iid&&) = delete;
+};
+
+#endif // IID_INCLUDED
diff --git a/src/irc/irc_channel.cpp b/src/irc/irc_channel.cpp
new file mode 100644
index 0000000..223305b
--- /dev/null
+++ b/src/irc/irc_channel.cpp
@@ -0,0 +1,24 @@
+#include <irc/irc_channel.hpp>
+#include <utils/make_unique.hpp>
+
+IrcChannel::IrcChannel():
+ joined(false),
+ self(nullptr)
+{
+}
+
+void IrcChannel::set_self(const std::string& name)
+{
+ this->self = std::make_unique<IrcUser>(name);
+}
+
+IrcUser* IrcChannel::add_user(const std::string& name)
+{
+ this->users.emplace_back(std::make_unique<IrcUser>(name));
+ return this->users.back().get();
+}
+
+IrcUser* IrcChannel::get_self() const
+{
+ return this->self.get();
+}
diff --git a/src/irc/irc_channel.hpp b/src/irc/irc_channel.hpp
new file mode 100644
index 0000000..da0f298
--- /dev/null
+++ b/src/irc/irc_channel.hpp
@@ -0,0 +1,34 @@
+#ifndef IRC_CHANNEL_INCLUDED
+# define IRC_CHANNEL_INCLUDED
+
+#include <irc/irc_user.hpp>
+#include <memory>
+#include <string>
+#include <vector>
+
+/**
+ * Keep the state of a joined channel (the list of occupants with their
+ * informations (mode, etc), the modes, etc)
+ */
+class IrcChannel
+{
+public:
+ explicit IrcChannel();
+
+ bool joined;
+ std::string topic;
+ void set_self(const std::string& name);
+ IrcUser* get_self() const;
+ IrcUser* add_user(const std::string& name);
+
+private:
+ std::unique_ptr<IrcUser> self;
+ std::vector<std::unique_ptr<IrcUser>> users;
+
+ IrcChannel(const IrcChannel&) = delete;
+ IrcChannel(IrcChannel&&) = delete;
+ IrcChannel& operator=(const IrcChannel&) = delete;
+ IrcChannel& operator=(IrcChannel&&) = delete;
+};
+
+#endif // IRC_CHANNEL_INCLUDED
diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp
index 80f36ee..7875b1c 100644
--- a/src/irc/irc_client.cpp
+++ b/src/irc/irc_client.cpp
@@ -1,10 +1,18 @@
-#include <irc/irc_client.hpp>
#include <irc/irc_message.hpp>
+#include <irc/irc_client.hpp>
+#include <bridge/bridge.hpp>
+#include <irc/irc_user.hpp>
+
+#include <utils/make_unique.hpp>
+#include <utils/split.hpp>
#include <iostream>
#include <stdexcept>
-IrcClient::IrcClient()
+IrcClient::IrcClient(const std::string& hostname, const std::string& username, Bridge* bridge):
+ hostname(hostname),
+ username(username),
+ bridge(bridge)
{
std::cout << "IrcClient()" << std::endl;
}
@@ -14,8 +22,15 @@ IrcClient::~IrcClient()
std::cout << "~IrcClient()" << std::endl;
}
+void IrcClient::start()
+{
+ this->connect(this->hostname, "6667");
+}
+
void IrcClient::on_connected()
{
+ this->send_nick_command(this->username);
+ this->send_user_command(this->username, this->username);
}
void IrcClient::on_connection_close()
@@ -23,6 +38,19 @@ void IrcClient::on_connection_close()
std::cout << "Connection closed by remote server." << std::endl;
}
+IrcChannel* IrcClient::get_channel(const std::string& name)
+{
+ try
+ {
+ return this->channels.at(name).get();
+ }
+ catch (const std::out_of_range& exception)
+ {
+ this->channels.emplace(name, std::make_unique<IrcChannel>());
+ return this->channels.at(name).get();
+ }
+}
+
void IrcClient::parse_in_buffer()
{
while (true)
@@ -33,6 +61,21 @@ void IrcClient::parse_in_buffer()
IrcMessage message(this->in_buf.substr(0, pos));
this->in_buf = this->in_buf.substr(pos + 2, std::string::npos);
std::cout << message << std::endl;
+ // TODO map function and command name properly
+ if (message.command == "PING")
+ this->send_pong_command();
+ else if (message.command == "NOTICE" ||
+ message.command == "375" ||
+ message.command == "372")
+ this->forward_server_message(message);
+ else if (message.command == "JOIN")
+ this->on_self_channel_join(message);
+ else if (message.command == "353")
+ this->set_and_forward_user_list(message);
+ else if (message.command == "332")
+ this->on_topic_received(message);
+ else if (message.command == "366")
+ this->on_channel_completely_joined(message);
}
}
@@ -52,6 +95,8 @@ void IrcClient::send_message(IrcMessage&& message)
res += " " + arg;
}
res += "\r\n";
+ std::cout << "=== IRC SENDING ===" << std::endl;
+ std::cout << res << std::endl;
this->send_data(std::move(res));
}
@@ -67,5 +112,59 @@ void IrcClient::send_nick_command(const std::string& nick)
void IrcClient::send_join_command(const std::string& chan_name)
{
- this->send_message(IrcMessage("JOIN", {chan_name}));
+ IrcChannel* channel = this->get_channel(chan_name);
+ if (channel->joined == false)
+ this->send_message(IrcMessage("JOIN", {chan_name}));
+}
+
+void IrcClient::send_pong_command()
+{
+ this->send_message(IrcMessage("PONG", {}));
+}
+
+void IrcClient::forward_server_message(const IrcMessage& message)
+{
+ const std::string from = message.prefix;
+ const std::string body = message.arguments[1];
+
+ this->bridge->send_xmpp_message(this->hostname, from, body);
+}
+
+void IrcClient::set_and_forward_user_list(const IrcMessage& message)
+{
+ const std::string chan_name = message.arguments[2];
+ IrcChannel* channel = this->get_channel(chan_name);
+ std::vector<std::string> nicks = utils::split(message.arguments[3], ' ');
+ for (const std::string& nick: nicks)
+ {
+ IrcUser* user = channel->add_user(nick);
+ if (user->nick != channel->get_self()->nick)
+ {
+ std::cout << "Adding user [" << nick << "] to chan " << chan_name << std::endl;
+ this->bridge->send_user_join(this->hostname, chan_name, user->nick);
+ }
+ }
+}
+
+void IrcClient::on_self_channel_join(const IrcMessage& message)
+{
+ const std::string chan_name = message.arguments[0];
+ IrcChannel* channel = this->get_channel(chan_name);
+ channel->joined = true;
+ channel->set_self(message.prefix);
+}
+
+void IrcClient::on_topic_received(const IrcMessage& message)
+{
+ const std::string chan_name = message.arguments[1];
+ IrcChannel* channel = this->get_channel(chan_name);
+ channel->topic = message.arguments[2];
+}
+
+void IrcClient::on_channel_completely_joined(const IrcMessage& message)
+{
+ const std::string chan_name = message.arguments[1];
+ IrcChannel* channel = this->get_channel(chan_name);
+ this->bridge->send_self_join(this->hostname, chan_name, channel->get_self()->nick);
+ this->bridge->send_topic(this->hostname, chan_name, channel->topic);
}
diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp
index e380f5b..db1b83b 100644
--- a/src/irc/irc_client.hpp
+++ b/src/irc/irc_client.hpp
@@ -2,11 +2,16 @@
# define IRC_CLIENT_INCLUDED
#include <irc/irc_message.hpp>
+#include <irc/irc_channel.hpp>
+#include <irc/iid.hpp>
#include <network/socket_handler.hpp>
+#include <unordered_map>
#include <string>
+class Bridge;
+
/**
* Represent one IRC client, i.e. an endpoint connected to a single IRC
* server, through a TCP socket, receiving and sending commands to it.
@@ -16,9 +21,13 @@
class IrcClient: public SocketHandler
{
public:
- explicit IrcClient();
+ explicit IrcClient(const std::string& hostname, const std::string& username, Bridge* bridge);
~IrcClient();
/**
+ * Connect to the IRC server
+ */
+ void start();
+ /**
* Called when successfully connected to the server
*/
void on_connected();
@@ -32,12 +41,20 @@ public:
*/
void parse_in_buffer();
/**
+ * Return the channel with this name, create it if it does not yet exist
+ */
+ IrcChannel* get_channel(const std::string& name);
+ /**
* Serialize the given message into a line, and send that into the socket
* (actually, into our out_buf and signal the poller that we want to wach
* for send events to be ready)
*/
void send_message(IrcMessage&& message);
/**
+ * Send the PONG irc command
+ */
+ void send_pong_command();
+ /**
* Send the USER irc command
*/
void send_user_command(const std::string& username, const std::string& realname);
@@ -49,8 +66,49 @@ public:
* Send the JOIN irc command
*/
void send_join_command(const std::string& chan_name);
+ /**
+ * Forward the server message received from IRC to the XMPP component
+ */
+ void forward_server_message(const IrcMessage& message);
+ /**
+ * Forward the join of an other user into an IRC channel, and save the
+ * IrcUsers in the IrcChannel
+ */
+ void set_and_forward_user_list(const IrcMessage& message);
+ /**
+ * Remember our nick and host, when we are joined to the channel. The list
+ * of user comes after so we do not send the self-presence over XMPP yet.
+ */
+ void on_self_channel_join(const IrcMessage& message);
+ /**
+ * Save the topic in the IrcChannel
+ */
+ void on_topic_received(const IrcMessage& message);
+ /**
+ * The channel has been completely joined (self presence, topic, all names
+ * received etc), send the self presence and topic to the XMPP user.
+ */
+ void on_channel_completely_joined(const IrcMessage& message);
private:
+ /**
+ * The hostname of the server we are connected to.
+ */
+ const std::string hostname;
+ /**
+ * The user name used in the USER irc command
+ */
+ const std::string username;
+ /**
+ * Raw pointer because the bridge owns us.
+ */
+ Bridge* bridge;
+
+ /**
+ * The list of joined channels, indexed by name
+ */
+ std::unordered_map<std::string, std::unique_ptr<IrcChannel>> channels;
+
IrcClient(const IrcClient&) = delete;
IrcClient(IrcClient&&) = delete;
IrcClient& operator=(const IrcClient&) = delete;
diff --git a/src/irc/irc_user.cpp b/src/irc/irc_user.cpp
new file mode 100644
index 0000000..fc853bc
--- /dev/null
+++ b/src/irc/irc_user.cpp
@@ -0,0 +1,24 @@
+#include <irc/irc_user.hpp>
+
+#include <iostream>
+
+IrcUser::IrcUser(const std::string& name)
+{
+ const std::string::size_type sep = name.find("!");
+ if (sep == std::string::npos)
+ {
+ if (name[0] == '@' || name[0] == '+')
+ this->nick = name.substr(1);
+ else
+ this->nick = name;
+ }
+ else
+ {
+ if (name[0] == '@' || name[0] == '+')
+ this->nick = name.substr(1, sep);
+ else
+ this->nick = name.substr(0, sep);
+ this->host = name.substr(sep+1);
+ }
+ std::cout << "Created user: [" << this->nick << "!" << this->host << std::endl;
+}
diff --git a/src/irc/irc_user.hpp b/src/irc/irc_user.hpp
new file mode 100644
index 0000000..b76b2ef
--- /dev/null
+++ b/src/irc/irc_user.hpp
@@ -0,0 +1,24 @@
+#ifndef IRC_USER_INCLUDED
+# define IRC_USER_INCLUDED
+
+#include <string>
+
+/**
+ * Keeps various information about one IRC channel user
+ */
+class IrcUser
+{
+public:
+ explicit IrcUser(const std::string& name);
+
+ std::string nick;
+ std::string host;
+
+private:
+ IrcUser(const IrcUser&) = delete;
+ IrcUser(IrcUser&&) = delete;
+ IrcUser& operator=(const IrcUser&) = delete;
+ IrcUser& operator=(IrcUser&&) = delete;
+};
+
+#endif // IRC_USER_INCLUDED
diff --git a/src/main.cpp b/src/main.cpp
index fe3b2e5..b0fb140 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,17 @@
#include <irc/irc_client.hpp>
+#include <xmpp/xmpp_component.hpp>
#include <network/poller.hpp>
+#include <xmpp/xmpp_parser.hpp>
+#include <xmpp/xmpp_stanza.hpp>
+
+#include <memory>
+
+#include <xmpp/jid.hpp>
+#include <irc/iid.hpp>
+
+#include <iostream>
+
int main()
{
Poller p;
diff --git a/src/xmpp/jid.cpp b/src/xmpp/jid.cpp
new file mode 100644
index 0000000..78b28a0
--- /dev/null
+++ b/src/xmpp/jid.cpp
@@ -0,0 +1,19 @@
+#include <xmpp/jid.hpp>
+
+Jid::Jid(const std::string& jid)
+{
+ std::string::size_type at = jid.find("@");
+ if (at != std::string::npos)
+ {
+ this->local = jid.substr(0, at);
+ at++;
+ }
+ else
+ at = 0;
+ std::string::size_type slash = jid.find("/", at);
+ if (slash != std::string::npos)
+ {
+ this->resource = jid.substr(slash + 1);
+ }
+ this->domain = jid.substr(at, slash - at);
+}
diff --git a/src/xmpp/jid.hpp b/src/xmpp/jid.hpp
new file mode 100644
index 0000000..3027497
--- /dev/null
+++ b/src/xmpp/jid.hpp
@@ -0,0 +1,26 @@
+#ifndef JID_INCLUDED
+# define JID_INCLUDED
+
+#include <string>
+
+/**
+ * Parse a JID into its different subart
+ */
+class Jid
+{
+public:
+ explicit Jid(const std::string& jid);
+
+ std::string domain;
+ std::string local;
+ std::string resource;
+
+private:
+ Jid(const Jid&) = delete;
+ Jid(Jid&&) = delete;
+ Jid& operator=(const Jid&) = delete;
+ Jid& operator=(Jid&&) = delete;
+};
+
+
+#endif // JID_INCLUDED
diff --git a/src/xmpp/stanza.hpp b/src/xmpp/stanza.hpp
new file mode 100644
index 0000000..697bda4
--- /dev/null
+++ b/src/xmpp/stanza.hpp
@@ -0,0 +1,18 @@
+#ifndef Stanza
+# define Stanza
+
+class Stanza
+{
+public:
+ explicit Stanza();
+ ~Stanza();
+private:
+ Stanza(const Stanza&) = delete;
+ Stanza(Stanza&&) = delete;
+ Stanza& operator=(const Stanza&) = delete;
+ Stanza& operator=(Stanza&&) = delete;
+};
+
+#endif // Stanza
+
+
diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp
index 0563aa7..3a288c7 100644
--- a/src/xmpp/xmpp_component.cpp
+++ b/src/xmpp/xmpp_component.cpp
@@ -1,4 +1,7 @@
+#include <utils/make_unique.hpp>
+
#include <xmpp/xmpp_component.hpp>
+#include <xmpp/jid.hpp>
#include <iostream>
@@ -20,6 +23,8 @@ XmppComponent::XmppComponent(const std::string& hostname, const std::string& sec
std::placeholders::_1));
this->stanza_handlers.emplace("handshake",
std::bind(&XmppComponent::handle_handshake, this,std::placeholders::_1));
+ this->stanza_handlers.emplace("presence",
+ std::bind(&XmppComponent::handle_presence, this,std::placeholders::_1));
}
XmppComponent::~XmppComponent()
@@ -46,7 +51,6 @@ void XmppComponent::on_connected()
node["xmlns:stream"] = "http://etherx.jabber.org/streams";
node["to"] = "irc.abricot";
this->send_stanza(node);
-
}
void XmppComponent::on_connection_close()
@@ -99,16 +103,17 @@ void XmppComponent::on_stanza(const Stanza& stanza)
{
std::cout << "=========== STANZA ============" << std::endl;
std::cout << stanza.to_string() << std::endl;
+ std::function<void(const Stanza&)> handler;
try
{
- const auto& handler = this->stanza_handlers.at(stanza.get_name());
- handler(stanza);
+ handler = this->stanza_handlers.at(stanza.get_name());
}
catch (const std::out_of_range& exception)
{
std::cout << "No handler for stanza of type " << stanza.get_name() << std::endl;
return;
}
+ handler(stanza);
}
void XmppComponent::send_stream_error(const std::string& name, const std::string& explanation)
@@ -133,5 +138,104 @@ void XmppComponent::close_document()
void XmppComponent::handle_handshake(const Stanza& stanza)
{
+ (void)stanza;
this->authenticated = true;
}
+
+void XmppComponent::handle_presence(const Stanza& stanza)
+{
+ Bridge* bridge = this->get_user_bridge(stanza["from"]);
+ Jid to(stanza["to"]);
+ Iid iid(to.local);
+ if (!iid.chan.empty() && !iid.server.empty())
+ bridge->join_irc_channel(iid, to.resource);
+}
+
+Bridge* XmppComponent::get_user_bridge(const std::string& user_jid)
+{
+ try
+ {
+ return this->bridges.at(user_jid).get();
+ }
+ catch (const std::out_of_range& exception)
+ {
+ this->bridges.emplace(user_jid, std::make_unique<Bridge>(user_jid, this, this->poller));
+ return this->bridges.at(user_jid).get();
+ }
+}
+
+void XmppComponent::send_message(const std::string& from, const std::string& body, const std::string& to)
+{
+ XmlNode node("message");
+ node["to"] = to;
+ node["from"] = from + "@" + this->served_hostname;
+ XmlNode body_node("body");
+ body_node.set_inner(body);
+ body_node.close();
+ node.add_child(std::move(body_node));
+ node.close();
+ this->send_stanza(node);
+}
+
+void XmppComponent::send_user_join(const std::string& from, const std::string& nick, const std::string& to)
+{
+ XmlNode node("presence");
+ node["to"] = to;
+ node["from"] = from + "@" + this->served_hostname + "/" + nick;
+
+ XmlNode x("x");
+ x["xmlns"] = "http://jabber.org/protocol/muc#user";
+
+ // TODO: put real values here
+ XmlNode item("item");
+ item["affiliation"] = "member";
+ item["role"] = "participant";
+ item.close();
+ x.add_child(std::move(item));
+ x.close();
+ node.add_child(std::move(x));
+ node.close();
+ this->send_stanza(node);
+}
+
+void XmppComponent::send_self_join(const std::string& from, const std::string& nick, const std::string& to)
+{
+ XmlNode node("presence");
+ node["to"] = to;
+ node["from"] = from + "@" + this->served_hostname + "/" + nick;
+
+ XmlNode x("x");
+ x["xmlns"] = "http://jabber.org/protocol/muc#user";
+
+ // TODO: put real values here
+ XmlNode item("item");
+ item["affiliation"] = "member";
+ item["role"] = "participant";
+ item.close();
+ x.add_child(std::move(item));
+
+ XmlNode status("status");
+ status["code"] = "110";
+ status.close();
+ x.add_child(std::move(status));
+
+ x.close();
+
+ node.add_child(std::move(x));
+ node.close();
+ this->send_stanza(node);
+}
+
+void XmppComponent::send_topic(const std::string& from, const std::string& topic, const std::string& to)
+{
+ XmlNode message("message");
+ message["to"] = to;
+ message["from"] = from + "@" + this->served_hostname;
+ message["type"] = "groupchat";
+ XmlNode subject("subject");
+ subject.set_inner(topic);
+ subject.close();
+ message.add_child(std::move(subject));
+ message.close();
+ this->send_stanza(message);
+}
diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp
index 464ecaa..725b495 100644
--- a/src/xmpp/xmpp_component.hpp
+++ b/src/xmpp/xmpp_component.hpp
@@ -1,13 +1,13 @@
#ifndef XMPP_COMPONENT_INCLUDED
# define XMPP_COMPONENT_INCLUDED
-#include <string>
-
#include <network/socket_handler.hpp>
-
#include <xmpp/xmpp_parser.hpp>
+#include <bridge/bridge.hpp>
#include <unordered_map>
+#include <memory>
+#include <string>
/**
* An XMPP component, communicating with an XMPP server using the protocole
@@ -55,13 +55,35 @@ public:
* Send the closing signal for our document (not closing the connection though).
*/
void close_document();
-
+ /**
+ * Send a message from from@served_hostname, with the given body
+ */
+ void send_message(const std::string& from, const std::string& body, const std::string& to);
+ /**
+ * Send a join from a new participant
+ */
+ void send_user_join(const std::string& from, const std::string& nick, const std::string& to);
+ /**
+ * Send the self join to the user
+ */
+ void send_self_join(const std::string& from, const std::string& nick, const std::string& to);
+ /**
+ * Send the MUC topic to the user
+ */
+ void send_topic(const std::string& from, const std::string& topic, const std::string& to);
/**
* Handle the various stanza types
*/
void handle_handshake(const Stanza& stanza);
+ void handle_presence(const Stanza& stanza);
private:
+ /**
+ * Return the bridge associated with the given full JID. Create a new one
+ * if none already exist.
+ */
+ Bridge* get_user_bridge(const std::string& user_jid);
+
XmppParser parser;
std::string stream_id;
std::string served_hostname;
@@ -70,6 +92,12 @@ private:
std::unordered_map<std::string, std::function<void(const Stanza&)>> stanza_handlers;
+ /**
+ * One bridge for each user of the component. Indexed by the user's full
+ * jid
+ */
+ std::unordered_map<std::string, std::unique_ptr<Bridge>> bridges;
+
XmppComponent(const XmppComponent&) = delete;
XmppComponent(XmppComponent&&) = delete;
XmppComponent& operator=(const XmppComponent&) = delete;