summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bridge/bridge.cpp43
-rw-r--r--src/bridge/bridge.hpp11
-rw-r--r--src/irc/iid.cpp4
-rw-r--r--src/irc/iid.hpp1
-rw-r--r--src/irc/irc_client.cpp59
-rw-r--r--src/irc/irc_client.hpp32
-rw-r--r--src/xmpp/stanza.hpp18
-rw-r--r--src/xmpp/xmpp_component.cpp30
-rw-r--r--src/xmpp/xmpp_component.hpp5
-rw-r--r--src/xmpp/xmpp_stanza.cpp37
-rw-r--r--src/xmpp/xmpp_stanza.hpp12
11 files changed, 224 insertions, 28 deletions
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp
index 638777d..5047a78 100644
--- a/src/bridge/bridge.cpp
+++ b/src/bridge/bridge.cpp
@@ -2,6 +2,8 @@
#include <xmpp/xmpp_component.hpp>
#include <network/poller.hpp>
+#include <iostream>
+
Bridge::Bridge(const std::string& user_jid, XmppComponent* xmpp, Poller* poller):
user_jid(user_jid),
xmpp(xmpp),
@@ -29,15 +31,54 @@ IrcClient* Bridge::get_irc_client(const std::string& hostname, const std::string
}
}
+IrcClient* Bridge::get_irc_client(const std::string& hostname)
+{
+ try
+ {
+ return this->irc_clients.at(hostname).get();
+ }
+ catch (const std::out_of_range& exception)
+ {
+ return nullptr;
+ }
+}
+
+
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_channel_message(const Iid& iid, const std::string& body)
+{
+ if (iid.chan.empty() || iid.server.empty())
+ {
+ std::cout << "Cannot send message to channel: [" << iid.chan << "] on server [" << iid.server << "]" << std::endl;
+ return;
+ }
+ IrcClient* irc = this->get_irc_client(iid.server);
+ if (!irc)
+ {
+ std::cout << "Cannot send message: no client exist for server " << iid.server << std::endl;
+ return;
+ }
+ irc->send_channel_message(iid.chan, body);
+ this->xmpp->send_muc_message(iid.chan + "%" + iid.server, irc->get_own_nick(), body, this->user_jid);
+}
+
+void Bridge::send_muc_message(const Iid& iid, const std::string& nick, const std::string& body)
+{
+ this->xmpp->send_muc_message(iid.chan + "%" + iid.server, nick, body, this->user_jid);
+}
+
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;
+ std::string body;
+ if (!author.empty())
+ body = std::string("[") + author + std::string("] ") + msg;
+ else
+ body = msg;
this->xmpp->send_message(from, body, this->user_jid);
}
diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp
index f9ddcca..38cf565 100644
--- a/src/bridge/bridge.hpp
+++ b/src/bridge/bridge.hpp
@@ -30,6 +30,7 @@ public:
**/
void join_irc_channel(const Iid& iid, const std::string& username);
+ void send_channel_message(const Iid& iid, const std::string& body);
/***
**
@@ -54,7 +55,10 @@ public:
* 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);
-
+ /**
+ * Send a MUC message from some participant
+ */
+ void send_muc_message(const Iid& iid, const std::string& nick, const std::string& body);
private:
/**
* Returns the client for the given hostname, create one (and use the
@@ -63,6 +67,11 @@ private:
*/
IrcClient* get_irc_client(const std::string& hostname, const std::string& username);
/**
+ * This version does not create the IrcClient if it does not exist, and
+ * returns nullptr in that case
+ */
+ IrcClient* get_irc_client(const std::string& hostname);
+ /**
* The JID of the user associated with this bridge. Messages from/to this
* JID are only managed by this bridge.
*/
diff --git a/src/irc/iid.cpp b/src/irc/iid.cpp
index ffc8d88..4694c0c 100644
--- a/src/irc/iid.cpp
+++ b/src/irc/iid.cpp
@@ -15,3 +15,7 @@ Iid::Iid(const std::string& iid)
}
this->server = iid.substr(sep);
}
+
+Iid::Iid()
+{
+}
diff --git a/src/irc/iid.hpp b/src/irc/iid.hpp
index aacc9e6..a62ac71 100644
--- a/src/irc/iid.hpp
+++ b/src/irc/iid.hpp
@@ -20,6 +20,7 @@ class Iid
{
public:
explicit Iid(const std::string& iid);
+ explicit Iid();
std::string chan;
std::string server;
diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp
index 7875b1c..cf57bd7 100644
--- a/src/irc/irc_client.cpp
+++ b/src/irc/irc_client.cpp
@@ -12,7 +12,9 @@
IrcClient::IrcClient(const std::string& hostname, const std::string& username, Bridge* bridge):
hostname(hostname),
username(username),
- bridge(bridge)
+ current_nick(username),
+ bridge(bridge),
+ welcomed(false)
{
std::cout << "IrcClient()" << std::endl;
}
@@ -51,6 +53,11 @@ IrcChannel* IrcClient::get_channel(const std::string& name)
}
}
+std::string IrcClient::get_own_nick() const
+{
+ return this->current_nick;
+}
+
void IrcClient::parse_in_buffer()
{
while (true)
@@ -63,19 +70,23 @@ void IrcClient::parse_in_buffer()
std::cout << message << std::endl;
// TODO map function and command name properly
if (message.command == "PING")
- this->send_pong_command();
+ this->send_pong_command(message);
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 == "PRIVMSG")
+ this->on_channel_message(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);
+ else if (message.command == "001")
+ this->on_welcome_message(message);
}
}
@@ -102,7 +113,7 @@ void IrcClient::send_message(IrcMessage&& message)
void IrcClient::send_user_command(const std::string& username, const std::string& realname)
{
- this->send_message(IrcMessage("USER", {username, "NONE", "NONE", realname}));
+ this->send_message(IrcMessage("USER", {username, "ignored", "ignored", realname}));
}
void IrcClient::send_nick_command(const std::string& nick)
@@ -112,14 +123,32 @@ void IrcClient::send_nick_command(const std::string& nick)
void IrcClient::send_join_command(const std::string& chan_name)
{
+ if (this->welcomed == false)
+ {
+ this->channels_to_join.push_back(chan_name);
+ return ;
+ }
IrcChannel* channel = this->get_channel(chan_name);
if (channel->joined == false)
this->send_message(IrcMessage("JOIN", {chan_name}));
}
-void IrcClient::send_pong_command()
+bool IrcClient::send_channel_message(const std::string& chan_name, const std::string& body)
{
- this->send_message(IrcMessage("PONG", {}));
+ IrcChannel* channel = this->get_channel(chan_name);
+ if (channel->joined == false)
+ {
+ std::cout << "Cannot send message to channel " << chan_name << ", it is not joined" << std::endl;
+ return false;
+ }
+ this->send_message(IrcMessage("PRIVMSG", {chan_name, body}));
+ return true;
+}
+
+void IrcClient::send_pong_command(const IrcMessage& message)
+{
+ const std::string id = message.arguments[0];
+ this->send_message(IrcMessage("PONG", {id}));
}
void IrcClient::forward_server_message(const IrcMessage& message)
@@ -154,6 +183,17 @@ void IrcClient::on_self_channel_join(const IrcMessage& message)
channel->set_self(message.prefix);
}
+void IrcClient::on_channel_message(const IrcMessage& message)
+{
+ const IrcUser user(message.prefix);
+ const std::string nick = user.nick;
+ Iid iid;
+ iid.chan = message.arguments[0];
+ iid.server = this->hostname;
+ const std::string body = message.arguments[1];
+ this->bridge->send_muc_message(iid, nick, body);
+}
+
void IrcClient::on_topic_received(const IrcMessage& message)
{
const std::string chan_name = message.arguments[1];
@@ -168,3 +208,12 @@ void IrcClient::on_channel_completely_joined(const IrcMessage& message)
this->bridge->send_self_join(this->hostname, chan_name, channel->get_self()->nick);
this->bridge->send_topic(this->hostname, chan_name, channel->topic);
}
+
+void IrcClient::on_welcome_message(const IrcMessage& message)
+{
+ this->current_nick = message.arguments[0];
+ this->welcomed = true;
+ for (const std::string& chan_name: this->channels_to_join)
+ this->send_join_command(chan_name);
+ this->channels_to_join.clear();
+}
diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp
index db1b83b..50f3781 100644
--- a/src/irc/irc_client.hpp
+++ b/src/irc/irc_client.hpp
@@ -45,6 +45,10 @@ public:
*/
IrcChannel* get_channel(const std::string& name);
/**
+ * Return our own nick
+ */
+ std::string get_own_nick() const;
+ /**
* 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)
@@ -53,7 +57,7 @@ public:
/**
* Send the PONG irc command
*/
- void send_pong_command();
+ void send_pong_command(const IrcMessage& message);
/**
* Send the USER irc command
*/
@@ -67,6 +71,11 @@ public:
*/
void send_join_command(const std::string& chan_name);
/**
+ * Send a PRIVMSG command for a channel
+ * Return true if the message was actually sent
+ */
+ bool send_channel_message(const std::string& chan_name, const std::string& body);
+ /**
* Forward the server message received from IRC to the XMPP component
*/
void forward_server_message(const IrcMessage& message);
@@ -81,6 +90,10 @@ public:
*/
void on_self_channel_join(const IrcMessage& message);
/**
+ * When a channel message is received
+ */
+ void on_channel_message(const IrcMessage& message);
+ /**
* Save the topic in the IrcChannel
*/
void on_topic_received(const IrcMessage& message);
@@ -89,6 +102,10 @@ public:
* received etc), send the self presence and topic to the XMPP user.
*/
void on_channel_completely_joined(const IrcMessage& message);
+ /**
+ * When a message 001 is received, join the rooms we wanted to join, and set our actual nickname
+ */
+ void on_welcome_message(const IrcMessage& message);
private:
/**
@@ -100,14 +117,25 @@ private:
*/
const std::string username;
/**
+ * Our current nickname on the server
+ */
+ std::string current_nick;
+ /**
* 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;
+ /**
+ * A list of chan we want to join, but we need a response 001 from
+ * the server before sending the actual JOIN commands. So we just keep the
+ * channel names in a list, and send the JOIN commands for each of them
+ * whenever the WELCOME message is received.
+ */
+ std::vector<std::string> channels_to_join;
+ bool welcomed;
IrcClient(const IrcClient&) = delete;
IrcClient(IrcClient&&) = delete;
diff --git a/src/xmpp/stanza.hpp b/src/xmpp/stanza.hpp
deleted file mode 100644
index 697bda4..0000000
--- a/src/xmpp/stanza.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#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 3a288c7..cd9cd6f 100644
--- a/src/xmpp/xmpp_component.cpp
+++ b/src/xmpp/xmpp_component.cpp
@@ -25,6 +25,8 @@ XmppComponent::XmppComponent(const std::string& hostname, const std::string& sec
std::bind(&XmppComponent::handle_handshake, this,std::placeholders::_1));
this->stanza_handlers.emplace("presence",
std::bind(&XmppComponent::handle_presence, this,std::placeholders::_1));
+ this->stanza_handlers.emplace("message",
+ std::bind(&XmppComponent::handle_message, this,std::placeholders::_1));
}
XmppComponent::~XmppComponent()
@@ -151,6 +153,20 @@ void XmppComponent::handle_presence(const Stanza& stanza)
bridge->join_irc_channel(iid, to.resource);
}
+void XmppComponent::handle_message(const Stanza& stanza)
+{
+ Bridge* bridge = this->get_user_bridge(stanza["from"]);
+ Jid to(stanza["to"]);
+ Iid iid(to.local);
+ XmlNode* body = stanza.get_child("body");
+ if (stanza["type"] == "groupchat")
+ {
+ if (to.resource.empty())
+ if (body && !body->get_inner().empty())
+ bridge->send_channel_message(iid, body->get_inner());
+ }
+}
+
Bridge* XmppComponent::get_user_bridge(const std::string& user_jid)
{
try
@@ -239,3 +255,17 @@ void XmppComponent::send_topic(const std::string& from, const std::string& topic
message.close();
this->send_stanza(message);
}
+
+void XmppComponent::send_muc_message(const std::string& muc_name, const std::string& nick, const std::string body_str, const std::string& jid_to)
+{
+ Stanza message("message");
+ message["to"] = jid_to;
+ message["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
+ message["type"] = "groupchat";
+ XmlNode body("body");
+ body.set_inner(body_str);
+ body.close();
+ message.add_child(std::move(body));
+ message.close();
+ this->send_stanza(message);
+}
diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp
index 725b495..73eadd2 100644
--- a/src/xmpp/xmpp_component.hpp
+++ b/src/xmpp/xmpp_component.hpp
@@ -72,10 +72,15 @@ public:
*/
void send_topic(const std::string& from, const std::string& topic, const std::string& to);
/**
+ * Send a (non-private) message to the MUC
+ */
+ void send_muc_message(const std::string& muc_name, const std::string& nick, const std::string body_str, const std::string& jid_to);
+ /**
* Handle the various stanza types
*/
void handle_handshake(const Stanza& stanza);
void handle_presence(const Stanza& stanza);
+ void handle_message(const Stanza& stanza);
private:
/**
diff --git a/src/xmpp/xmpp_stanza.cpp b/src/xmpp/xmpp_stanza.cpp
index 2c98acc..4c0088e 100644
--- a/src/xmpp/xmpp_stanza.cpp
+++ b/src/xmpp/xmpp_stanza.cpp
@@ -2,6 +2,26 @@
#include <iostream>
+std::string xml_escape(const std::string& data)
+{
+ std::string res;
+ buffer.reserve(data.size());
+ for(size_t pos = 0; pos != data.size(); ++pos)
+ {
+ switch(data[pos])
+ {
+ case '&': buffer += "&amp;"; break;
+ case '\"': buffer += "&quot;"; break;
+ case '\'': buffer += "&apos;"; break;
+ case '<': buffer += "&lt;"; break;
+ case '>': buffer += "&gt;"; break;
+ default: buffer += data[pos]; break;
+ }
+ }
+ return buffer;
+}
+
+
XmlNode::XmlNode(const std::string& name, XmlNode* parent):
name(name),
parent(parent),
@@ -40,7 +60,22 @@ void XmlNode::set_tail(const std::string& data)
void XmlNode::set_inner(const std::string& data)
{
- this->inner = data;
+ this->inner = xml_escape(data);
+}
+
+std::string XmlNode::get_inner() const
+{
+ return this->inner;
+}
+
+XmlNode* XmlNode::get_child(const std::string& name) const
+{
+ for (auto& child: this->children)
+ {
+ if (child->name == name)
+ return child;
+ }
+ return nullptr;
}
void XmlNode::add_child(XmlNode* child)
diff --git a/src/xmpp/xmpp_stanza.hpp b/src/xmpp/xmpp_stanza.hpp
index 277b0db..62f152d 100644
--- a/src/xmpp/xmpp_stanza.hpp
+++ b/src/xmpp/xmpp_stanza.hpp
@@ -7,6 +7,8 @@
#include <expatpp.h>
+std::string xml_escape(const std::string& data);
+
/**
* Raised on operator[] when the attribute does not exist
*/
@@ -51,8 +53,18 @@ public:
void set_tail(const std::string& data);
/**
* Set the content of the inner, that is the text inside this node
+ * TODO: escape it here.
*/
void set_inner(const std::string& data);
+ /**
+ * Get the content of inner
+ * TODO: unescape it here.
+ */
+ std::string get_inner() const;
+ /**
+ * Get a pointer to the first child element with that name
+ */
+ XmlNode* get_child(const std::string& name) const;
void add_child(XmlNode* child);
void add_child(XmlNode&& child);
XmlNode* get_last_child() const;