diff options
-rw-r--r-- | src/bridge/bridge.cpp | 20 | ||||
-rw-r--r-- | src/irc/irc_channel.cpp | 6 | ||||
-rw-r--r-- | src/irc/irc_channel.hpp | 33 | ||||
-rw-r--r-- | src/irc/irc_client.cpp | 23 | ||||
-rw-r--r-- | src/irc/irc_client.hpp | 17 | ||||
-rw-r--r-- | src/test.cpp | 1 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 5 |
7 files changed, 101 insertions, 4 deletions
diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 277f696..da10e28 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -1,6 +1,7 @@ #include <bridge/bridge.hpp> #include <bridge/colors.hpp> #include <xmpp/xmpp_component.hpp> +#include <irc/irc_message.hpp> #include <network/poller.hpp> #include <utils/encoding.hpp> #include <logger/logger.hpp> @@ -100,6 +101,25 @@ IrcClient* Bridge::get_irc_client(const std::string& hostname) bool Bridge::join_irc_channel(const Iid& iid, const std::string& username) { IrcClient* irc = this->get_irc_client(iid.server, username); + if (iid.chan.empty()) + { // Join the dummy channel + if (irc->is_welcomed()) + { + if (irc->get_dummy_channel().joined) + return false; + // Immediately simulate a message coming from the IRC server saying that we + // joined the channel + const IrcMessage join_message(irc->get_nick(), "JOIN", {""}); + irc->on_channel_join(join_message); + const IrcMessage end_join_message(std::string(iid.server), "366", + {irc->get_nick(), + "", "End of NAMES list"}); + irc->on_channel_completely_joined(end_join_message); + } + else + irc->get_dummy_channel().joining = true; + return true; + } if (irc->is_channel_joined(iid.chan) == false) { irc->send_join_command(iid.chan); diff --git a/src/irc/irc_channel.cpp b/src/irc/irc_channel.cpp index 99783fa..0604528 100644 --- a/src/irc/irc_channel.cpp +++ b/src/irc/irc_channel.cpp @@ -47,3 +47,9 @@ void IrcChannel::remove_user(const IrcUser* user) } } } + +DummyIrcChannel::DummyIrcChannel(): + IrcChannel(), + joining(false) +{ +} diff --git a/src/irc/irc_channel.hpp b/src/irc/irc_channel.hpp index 0160469..ab04d60 100644 --- a/src/irc/irc_channel.hpp +++ b/src/irc/irc_channel.hpp @@ -25,13 +25,44 @@ public: IrcUser* find_user(const std::string& name) const; void remove_user(const IrcUser* user); -private: +protected: std::unique_ptr<IrcUser> self; std::vector<std::unique_ptr<IrcUser>> users; + +private: IrcChannel(const IrcChannel&) = delete; IrcChannel(IrcChannel&&) = delete; IrcChannel& operator=(const IrcChannel&) = delete; IrcChannel& operator=(IrcChannel&&) = delete; }; +/** + * A special channel that is not actually linked to any real irc + * channel. This is just a channel representing a connection to the + * server. If an user wants to maintain the connection to the server without + * having to be on any irc channel of that server, he can just join this + * dummy channel. + * It’s not actually dummy because it’s useful and it does things, but well. + */ +class DummyIrcChannel: public IrcChannel +{ +public: + explicit DummyIrcChannel(); + + /** + * This flag is at true whenever the user wants to join this channel, but + * he is not yet connected to the server. When the connection is made, we + * check that flag and if it’s true, we inform the user that he has just + * joined that channel. + * If the user is already connected to the server when he tries to join + * the channel, we don’t use that flag, we just join it immediately. + */ + bool joining; +private: + DummyIrcChannel(const DummyIrcChannel&) = delete; + DummyIrcChannel(DummyIrcChannel&&) = delete; + DummyIrcChannel& operator=(const DummyIrcChannel&) = delete; + DummyIrcChannel& operator=(DummyIrcChannel&&) = delete; +}; + #endif // IRC_CHANNEL_INCLUDED diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 884f214..e565658 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -280,7 +280,11 @@ void IrcClient::set_and_forward_user_list(const IrcMessage& message) void IrcClient::on_channel_join(const IrcMessage& message) { const std::string chan_name = utils::tolower(message.arguments[0]); - IrcChannel* channel = this->get_channel(chan_name); + IrcChannel* channel; + if (chan_name.empty()) + channel = &this->dummy_channel; + else + channel = this->get_channel(chan_name); const std::string nick = message.prefix; if (channel->joined == false) channel->set_self(nick); @@ -394,6 +398,18 @@ void IrcClient::on_welcome_message(const IrcMessage& message) for (const std::string& chan_name: this->channels_to_join) this->send_join_command(chan_name); this->channels_to_join.clear(); + // Indicate that the dummy channel is joined as well, if needed + if (this->dummy_channel.joining) + { + // Simulate a message coming from the IRC server saying that we joined + // the channel + const IrcMessage join_message(this->get_nick(), "JOIN", {""}); + this->on_channel_join(join_message); + const IrcMessage end_join_message(std::string(this->hostname), "366", + {this->get_nick(), + "", "End of NAMES list"}); + this->on_channel_completely_joined(end_join_message); + } } void IrcClient::on_part(const IrcMessage& message) @@ -624,3 +640,8 @@ size_t IrcClient::number_of_joined_channels() const { return this->channels.size(); } + +DummyIrcChannel& IrcClient::get_dummy_channel() +{ + return this->dummy_channel; +} diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index 908db8e..960d36f 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -194,6 +194,14 @@ public: * Return the number of joined channels */ size_t number_of_joined_channels() const; + /** + * Get a reference to the unique dummy channel + */ + DummyIrcChannel& get_dummy_channel(); + + const std::string& get_hostname() const { return this->hostname; } + std::string get_nick() const { return this->current_nick; } + bool is_welcomed() const { return this->welcomed; } private: /** @@ -217,12 +225,21 @@ private: */ std::unordered_map<std::string, std::unique_ptr<IrcChannel>> channels; /** + * A single channel with a iid of the form "hostname" (normal channel have + * an iid of the form "chan%hostname". + */ + DummyIrcChannel dummy_channel; + /** * 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; + /** + * This flag indicates that the server is completely joined (connection + * has been established, we are authentified and we have a nick) + */ bool welcomed; /** * See http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt section 3.3 diff --git a/src/test.cpp b/src/test.cpp index 7a2051f..e66c4ad 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -10,6 +10,7 @@ #include <utils/tolower.hpp> #include <utils/split.hpp> #include <xmpp/jid.hpp> +#include <irc/iid.hpp> #include <string.h> #include <iostream> diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 606be10..8e8cc91 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -287,8 +287,9 @@ void XmppComponent::handle_presence(const Stanza& stanza) error_type, error_name, ""); }); - if (!iid.chan.empty() && !iid.server.empty()) - { // presence toward a MUC that corresponds to an irc channel + if (!iid.server.empty()) + { // presence toward a MUC that corresponds to an irc channel, or a + // dummy channel if iid.chan is empty if (type.empty()) { const std::string own_nick = bridge->get_own_nick(iid); |