diff options
Diffstat (limited to 'src/irc')
-rw-r--r-- | src/irc/iid.cpp | 17 | ||||
-rw-r--r-- | src/irc/iid.hpp | 34 | ||||
-rw-r--r-- | src/irc/irc_channel.cpp | 24 | ||||
-rw-r--r-- | src/irc/irc_channel.hpp | 34 | ||||
-rw-r--r-- | src/irc/irc_client.cpp | 105 | ||||
-rw-r--r-- | src/irc/irc_client.hpp | 60 | ||||
-rw-r--r-- | src/irc/irc_user.cpp | 24 | ||||
-rw-r--r-- | src/irc/irc_user.hpp | 24 |
8 files changed, 318 insertions, 4 deletions
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 |