diff options
author | Jonas Smedegaard <dr@jones.dk> | 2017-06-24 09:21:37 +0200 |
---|---|---|
committer | Jonas Smedegaard <dr@jones.dk> | 2017-06-24 09:21:37 +0200 |
commit | 3d39f109ba8ea7ae9778c58bd1665b9e8e0f45cb (patch) | |
tree | 9a5684babcd16d302fbe59c56b6045660ad62488 /src/irc/irc_client.cpp | |
parent | f9cee98aacd6aea8ccb7f5677b4ff1e1e234e4d1 (diff) | |
parent | c21cbbf9667991d2b928562a9c199e625d3f9bba (diff) | |
download | biboumi-3d39f109ba8ea7ae9778c58bd1665b9e8e0f45cb.tar.gz biboumi-3d39f109ba8ea7ae9778c58bd1665b9e8e0f45cb.tar.bz2 biboumi-3d39f109ba8ea7ae9778c58bd1665b9e8e0f45cb.tar.xz biboumi-3d39f109ba8ea7ae9778c58bd1665b9e8e0f45cb.zip |
Updated version 5.0 from 'upstream/5.0'
with Debian dir 2ae31d03ffb1d79153a692af23c7b2b097cc4b2b
Diffstat (limited to 'src/irc/irc_client.cpp')
-rw-r--r-- | src/irc/irc_client.cpp | 211 |
1 files changed, 154 insertions, 57 deletions
diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index de6b089..1d74098 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -1,3 +1,4 @@ +#include <utility> #include <utils/timed_events.hpp> #include <database/database.hpp> #include <irc/irc_message.hpp> @@ -14,13 +15,13 @@ #include <sstream> #include <iostream> #include <stdexcept> +#include <algorithm> #include <cstring> #include <chrono> #include <string> #include "biboumi.h" -#include "louloulibs.h" using namespace std::string_literals; using namespace std::chrono_literals; @@ -61,11 +62,14 @@ static const std::unordered_map<std::string, {"333", {&IrcClient::on_topic_who_time_received, {4, 0}}}, {"RPL_TOPICWHOTIME", {&IrcClient::on_topic_who_time_received, {4, 0}}}, {"366", {&IrcClient::on_channel_completely_joined, {2, 0}}}, + {"367", {&IrcClient::on_banlist, {3, 0}}}, + {"368", {&IrcClient::on_banlist_end, {3, 0}}}, {"396", {&IrcClient::on_own_host_received, {2, 0}}}, {"432", {&IrcClient::on_erroneous_nickname, {2, 0}}}, {"433", {&IrcClient::on_nickname_conflict, {2, 0}}}, {"438", {&IrcClient::on_nickname_change_too_fast, {2, 0}}}, {"443", {&IrcClient::on_useronchannel, {3, 0}}}, + {"475", {&IrcClient::on_channel_bad_key, {3, 0}}}, {"ERR_USERONCHANNEL", {&IrcClient::on_useronchannel, {3, 0}}}, {"001", {&IrcClient::on_welcome_message, {1, 0}}}, {"PART", {&IrcClient::on_part, {1, 0}}}, @@ -113,7 +117,6 @@ static const std::unordered_map<std::string, {"472", {&IrcClient::on_generic_error, {2, 0}}}, {"473", {&IrcClient::on_generic_error, {2, 0}}}, {"474", {&IrcClient::on_generic_error, {2, 0}}}, - {"475", {&IrcClient::on_generic_error, {2, 0}}}, {"476", {&IrcClient::on_generic_error, {2, 0}}}, {"477", {&IrcClient::on_generic_error, {2, 0}}}, {"481", {&IrcClient::on_generic_error, {2, 0}}}, @@ -127,16 +130,16 @@ static const std::unordered_map<std::string, {"502", {&IrcClient::on_generic_error, {2, 0}}}, }; -IrcClient::IrcClient(std::shared_ptr<Poller> poller, const std::string& hostname, - const std::string& nickname, const std::string& username, - const std::string& realname, const std::string& user_hostname, +IrcClient::IrcClient(std::shared_ptr<Poller>& poller, std::string hostname, + std::string nickname, std::string username, + std::string realname, std::string user_hostname, Bridge& bridge): - TCPSocketHandler(poller), - hostname(hostname), - user_hostname(user_hostname), - username(username), - realname(realname), - current_nick(nickname), + TCPClientSocketHandler(poller), + hostname(std::move(hostname)), + user_hostname(std::move(user_hostname)), + username(std::move(username)), + realname(std::move(realname)), + current_nick(std::move(nickname)), bridge(bridge), welcomed(false), chanmodes({"", "", "", ""}), @@ -343,7 +346,7 @@ void IrcClient::parse_in_buffer(const size_t) if (pos == std::string::npos) break ; IrcMessage message(this->in_buf.substr(0, pos)); - this->in_buf = this->in_buf.substr(pos + 2, std::string::npos); + this->consume_in_buffer(pos + 2); log_debug("IRC RECEIVING: (", this->get_hostname(), ") ", message); // Call the standard callback (if any), associated with the command @@ -386,10 +389,10 @@ void IrcClient::send_message(IrcMessage&& message) std::string res; if (!message.prefix.empty()) res += ":" + std::move(message.prefix) + " "; - res += std::move(message.command); + res += message.command; for (const std::string& arg: message.arguments) { - if (arg.find(" ") != std::string::npos || + if (arg.find(' ') != std::string::npos || (!arg.empty() && arg[0] == ':')) { res += " :" + arg; @@ -455,7 +458,12 @@ void IrcClient::send_quit_command(const std::string& reason) void IrcClient::send_join_command(const std::string& chan_name, const std::string& password) { if (this->welcomed == false) - this->channels_to_join.emplace_back(chan_name, password); + { + const auto it = std::find_if(begin(this->channels_to_join), end(this->channels_to_join), + [&chan_name](const auto& pair) { return std::get<0>(pair) == chan_name; }); + if (it == end(this->channels_to_join)) + this->channels_to_join.emplace_back(chan_name, password); + } else if (password.empty()) this->send_message(IrcMessage("JOIN", {chan_name})); else @@ -501,15 +509,7 @@ void IrcClient::send_private_message(const std::string& username, const std::str void IrcClient::send_part_command(const std::string& chan_name, const std::string& status_message) { - IrcChannel* channel = this->get_channel(chan_name); - if (channel->joined == true) - { - if (chan_name.empty()) - this->leave_dummy_channel(status_message); - else - this->send_message(IrcMessage("PART", {chan_name, status_message})); - channel->parting = true; - } + this->send_message(IrcMessage("PART", {chan_name, status_message})); } void IrcClient::send_mode_command(const std::string& chan_name, const std::vector<std::string>& arguments) @@ -546,9 +546,18 @@ void IrcClient::forward_server_message(const IrcMessage& message) void IrcClient::on_notice(const IrcMessage& message) { std::string from = message.prefix; - const std::string to = message.arguments[0]; + std::string to = message.arguments[0]; const std::string body = message.arguments[1]; + // Handle notices starting with [#channame] as if they were sent to that channel + if (body.size() > 3 && body[0] == '[') + { + const auto chan_prefix = body[1]; + auto end = body.find(']'); + if (this->chantypes.find(chan_prefix) != this->chantypes.end() && end != std::string::npos) + to = body.substr(1, end - 1); + } + if (!body.empty() && body[0] == '\01' && body[body.size() - 1] == '\01') // Do not forward the notice to the user if it's a CTCP command return ; @@ -635,15 +644,18 @@ void IrcClient::set_and_forward_user_list(const IrcMessage& message) std::vector<std::string> nicks = utils::split(message.arguments[3], ' '); for (const std::string& nick: nicks) { - const IrcUser* user = channel->add_user(nick, this->prefix_to_mode); - if (user->nick != channel->get_self()->nick) + // Just create this dummy user to parse and get its modes + IrcUser tmp_user{nick, this->prefix_to_mode}; + // Does this concern ourself + if (channel->get_self() && channel->find_user(tmp_user.nick) == channel->get_self()) { - this->bridge.send_user_join(this->hostname, chan_name, user, user->get_most_significant_mode(this->sorted_user_modes), false); + // We now know our own modes, that’s all. + channel->get_self()->modes = tmp_user.modes; } else - { - // we now know the modes of self, so copy the modes into self - channel->get_self()->modes = user->modes; + { // Otherwise this is a new user + const IrcUser *user = channel->add_user(nick, this->prefix_to_mode); + this->bridge.send_user_join(this->hostname, chan_name, user, user->get_most_significant_mode(this->sorted_user_modes), false); } } } @@ -657,13 +669,11 @@ void IrcClient::on_channel_join(const IrcMessage& message) else channel = this->get_channel(chan_name); const std::string nick = message.prefix; + IrcUser* user = channel->add_user(nick, this->prefix_to_mode); if (channel->joined == false) - channel->set_self(nick); + channel->set_self(user); else - { - const IrcUser* user = channel->add_user(nick, this->prefix_to_mode); - this->bridge.send_user_join(this->hostname, chan_name, user, user->get_most_significant_mode(this->sorted_user_modes), false); - } + this->bridge.send_user_join(this->hostname, chan_name, user, user->get_most_significant_mode(this->sorted_user_modes), false); } void IrcClient::on_channel_message(const IrcMessage& message) @@ -776,6 +786,43 @@ void IrcClient::on_channel_completely_joined(const IrcMessage& message) this->bridge.send_topic(this->hostname, chan_name, channel->topic, channel->topic_author); } +void IrcClient::on_banlist(const IrcMessage& message) +{ + const std::string chan_name = utils::tolower(message.arguments[1]); + IrcChannel* channel = this->get_channel(chan_name); + if (channel->joined) + { + Iid iid; + iid.set_local(chan_name); + iid.set_server(this->hostname); + iid.type = Iid::Type::Channel; + std::string body{message.arguments[2] + " banned"}; + if (message.arguments.size() >= 4) + { + IrcUser by(message.arguments[3], this->prefix_to_mode); + body += " by " + by.nick; + } + if (message.arguments.size() >= 5) + body += " on " + message.arguments[4]; + + this->bridge.send_message(iid, "", body, true); + } +} + +void IrcClient::on_banlist_end(const IrcMessage& message) +{ + const std::string chan_name = utils::tolower(message.arguments[1]); + IrcChannel* channel = this->get_channel(chan_name); + if (channel->joined) + { + Iid iid; + iid.set_local(chan_name); + iid.set_server(this->hostname); + iid.type = Iid::Type::Channel; + this->bridge.send_message(iid, "", message.arguments[2], true); + } +} + void IrcClient::on_own_host_received(const IrcMessage& message) { this->own_host = message.arguments[1]; @@ -799,10 +846,10 @@ void IrcClient::on_nickname_conflict(const IrcMessage& message) { const std::string nickname = message.arguments[1]; this->on_generic_error(message); - for (auto it = this->channels.begin(); it != this->channels.end(); ++it) + for (const auto& pair: this->channels) { Iid iid; - iid.set_local(it->first); + iid.set_local(pair.first); iid.set_server(this->hostname); iid.type = Iid::Type::Channel; this->bridge.send_nickname_conflict_error(iid, nickname); @@ -816,10 +863,10 @@ void IrcClient::on_nickname_change_too_fast(const IrcMessage& message) if (message.arguments.size() >= 3) txt = message.arguments[2]; this->on_generic_error(message); - for (auto it = this->channels.begin(); it != this->channels.end(); ++it) + for (const auto& pair: this->channels) { Iid iid; - iid.set_local(it->first); + iid.set_local(pair.first); iid.set_server(this->hostname); iid.type = Iid::Type::Channel; this->bridge.send_presence_error(iid, nickname, @@ -853,8 +900,47 @@ void IrcClient::on_welcome_message(const IrcMessage& message) // Install a repeated events to regularly send a PING TimedEventsManager::instance().add_event(TimedEvent(240s, std::bind(&IrcClient::send_ping_command, this), "PING"s + this->hostname + this->bridge.get_jid())); + std::string channels{}; + std::string channels_with_key{}; + std::string keys{}; + for (const auto& tuple: this->channels_to_join) - this->send_join_command(std::get<0>(tuple), std::get<1>(tuple)); + { + const auto& chan = std::get<0>(tuple); + const auto& key = std::get<1>(tuple); + if (chan.empty()) + continue; + if (!key.empty()) + { + if (keys.size() + channels_with_key.size() >= 300) + { // Arbitrary size, to make sure we never send more than 512 + this->send_join_command(channels_with_key, keys); + channels_with_key.clear(); + keys.clear(); + } + if (!keys.empty()) + keys += ","; + keys += key; + if (!channels_with_key.empty()) + channels_with_key += ","; + channels_with_key += chan; + } + else + { + if (channels.size() >= 300) + { // Arbitrary size, to make sure we never send more than 512 + this->send_join_command(channels, {}); + channels.clear(); + } + if (!channels.empty()) + channels += ","; + channels += chan; + } + } + if (!channels.empty()) + this->send_join_command(channels, {}); + if (!channels_with_key.empty()) + this->send_join_command(channels_with_key, keys); this->channels_to_join.clear(); // Indicate that the dummy channel is joined as well, if needed if (this->dummy_channel.joining) @@ -883,20 +969,19 @@ void IrcClient::on_part(const IrcMessage& message) if (user) { std::string nick = user->nick; + bool self = channel->get_self() && channel->get_self()->nick == nick; channel->remove_user(user); Iid iid; iid.set_local(chan_name); iid.set_server(this->hostname); iid.type = Iid::Type::Channel; - bool self = channel->get_self()->nick == nick; if (self) { - channel->joined = false; this->channels.erase(utils::tolower(chan_name)); // channel pointer is now invalid channel = nullptr; } - this->bridge.send_muc_leave(std::move(iid), std::move(nick), std::move(txt), self); + this->bridge.send_muc_leave(iid, std::move(nick), txt, self); } } @@ -904,17 +989,17 @@ void IrcClient::on_error(const IrcMessage& message) { const std::string leave_message = message.arguments[0]; // The user is out of all the channels - for (auto it = this->channels.begin(); it != this->channels.end(); ++it) + for (const auto& pair: this->channels) { Iid iid; - iid.set_local(it->first); + iid.set_local(pair.first); iid.set_server(this->hostname); iid.type = Iid::Type::Channel; - IrcChannel* channel = it->second.get(); + IrcChannel* channel = pair.second.get(); if (!channel->joined) continue; std::string own_nick = channel->get_self()->nick; - this->bridge.send_muc_leave(std::move(iid), std::move(own_nick), leave_message, true); + this->bridge.send_muc_leave(iid, std::move(own_nick), leave_message, true); } this->channels.clear(); this->send_gateway_message("ERROR: "s + leave_message); @@ -925,10 +1010,10 @@ void IrcClient::on_quit(const IrcMessage& message) std::string txt; if (message.arguments.size() >= 1) txt = message.arguments[0]; - for (auto it = this->channels.begin(); it != this->channels.end(); ++it) + for (const auto& pair: this->channels) { - const std::string chan_name = it->first; - IrcChannel* channel = it->second.get(); + const std::string& chan_name = pair.first; + IrcChannel* channel = pair.second.get(); const IrcUser* user = channel->find_user(message.prefix); if (user) { @@ -938,7 +1023,7 @@ void IrcClient::on_quit(const IrcMessage& message) iid.set_local(chan_name); iid.set_server(this->hostname); iid.type = Iid::Type::Channel; - this->bridge.send_muc_leave(std::move(iid), std::move(nick), txt, false); + this->bridge.send_muc_leave(iid, std::move(nick), txt, false); } } } @@ -974,9 +1059,9 @@ void IrcClient::on_nick(const IrcMessage& message) { change_nick_func("", &this->get_dummy_channel()); } - for (auto it = this->channels.begin(); it != this->channels.end(); ++it) + for (const auto& pair: this->channels) { - change_nick_func(it->first, it->second.get()); + change_nick_func(pair.first, pair.second.get()); } } @@ -1019,6 +1104,18 @@ void IrcClient::on_mode(const IrcMessage& message) this->on_user_mode(message); } +void IrcClient::on_channel_bad_key(const IrcMessage& message) +{ + this->on_generic_error(message); + const std::string& nickname = message.arguments[0]; + const std::string& channel = message.arguments[1]; + std::string text; + if (message.arguments.size() > 2) + text = message.arguments[2]; + + this->bridge.send_presence_error({channel, this->hostname, Iid::Type::Channel}, nickname, "auth", "not-authorized", "", text); +} + void IrcClient::on_channel_mode(const IrcMessage& message) { // For now, just transmit the modes so the user can know what happens @@ -1075,7 +1172,7 @@ void IrcClient::on_channel_mode(const IrcMessage& message) { // That mode can also be of type B if it is present in the // prefix_to_mode map - for (const std::pair<char, char>& pair: this->prefix_to_mode) + for (const auto& pair: this->prefix_to_mode) if (pair.second == c) { type = 1; @@ -1148,14 +1245,14 @@ DummyIrcChannel& IrcClient::get_dummy_channel() return this->dummy_channel; } -void IrcClient::leave_dummy_channel(const std::string& exit_message) +void IrcClient::leave_dummy_channel(const std::string& exit_message, const std::string& resource) { if (!this->dummy_channel.joined) return; this->dummy_channel.joined = false; this->dummy_channel.joining = false; this->dummy_channel.remove_all_users(); - this->bridge.send_muc_leave(Iid("%"s + this->hostname, this->chantypes), std::string(this->current_nick), exit_message, true); + this->bridge.send_muc_leave(Iid("%"s + this->hostname, this->chantypes), std::string(this->current_nick), exit_message, true, resource); } #ifdef BOTAN_FOUND |