Send and receive messages
Also correctly respond to PING with the id, escape some XML content, but not always
This commit is contained in:
+42
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -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
|
||||
@@ -62,6 +66,11 @@ private:
|
||||
* client immediately.
|
||||
*/
|
||||
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.
|
||||
|
||||
@@ -15,3 +15,7 @@ Iid::Iid(const std::string& iid)
|
||||
}
|
||||
this->server = iid.substr(sep);
|
||||
}
|
||||
|
||||
Iid::Iid()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ class Iid
|
||||
{
|
||||
public:
|
||||
explicit Iid(const std::string& iid);
|
||||
explicit Iid();
|
||||
|
||||
std::string chan;
|
||||
std::string server;
|
||||
|
||||
+54
-5
@@ -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();
|
||||
}
|
||||
|
||||
+30
-2
@@ -44,6 +44,10 @@ public:
|
||||
* Return the channel with this name, create it if it does not yet exist
|
||||
*/
|
||||
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
|
||||
@@ -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
|
||||
*/
|
||||
@@ -66,6 +70,11 @@ public:
|
||||
* Send the JOIN irc command
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -80,6 +89,10 @@ public:
|
||||
* of user comes after so we do not send the self-presence over XMPP yet.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -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:
|
||||
/**
|
||||
@@ -99,15 +116,26 @@ private:
|
||||
* The user name used in the USER irc command
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -71,11 +71,16 @@ public:
|
||||
* Send the MUC topic to the user
|
||||
*/
|
||||
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:
|
||||
/**
|
||||
|
||||
@@ -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 += "&"; break;
|
||||
case '\"': buffer += """; break;
|
||||
case '\'': buffer += "'"; break;
|
||||
case '<': buffer += "<"; break;
|
||||
case '>': buffer += ">"; 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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user