From 34fc1d4010d23be947c00fc956f2bdded2374cee Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 31 Oct 2015 06:17:35 +0100 Subject: Implement a basic webirc support See https://kiwiirc.com/docs/webirc fix #3135 --- doc/biboumi.1.md | 7 +++++++ src/bridge/bridge.cpp | 8 +++++--- src/irc/irc_client.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/irc/irc_client.hpp | 17 ++++++++++++++++- 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/doc/biboumi.1.md b/doc/biboumi.1.md index 763be56..1631ebd 100644 --- a/doc/biboumi.1.md +++ b/doc/biboumi.1.md @@ -96,6 +96,13 @@ The configuration file uses a simple format of the form username of each user will be set to the nick they used to connect to the IRC server. +`webirc_password` + + Configure a password to be communicated to the IRC server, as part of the + WEBIRC message (see https://kiwiirc.com/docs/webirc). If this option is + set, an additional DNS resolution of the hostname of each XMPP server will + be made when connecting to an IRC server. + `log_file` A filename into which logs are written. If none is provided, the logs are diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index 18a8d92..5badb18 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -56,7 +56,8 @@ void Bridge::clean() while (it != this->irc_clients.end()) { IrcClient* client = it->second.get(); - if (!client->is_connected() && !client->is_connecting()) + if (!client->is_connected() && !client->is_connecting() && + !client->get_resolver().is_resolving()) it = this->irc_clients.erase(it); else ++it; @@ -94,16 +95,17 @@ IrcClient* Bridge::make_irc_client(const std::string& hostname, const std::strin { auto username = nickname; auto realname = nickname; + Jid jid(this->user_jid); if (Config::get("realname_from_jid", "false") == "true") { - Jid jid(this->user_jid); username = jid.local; realname = this->get_bare_jid(); } this->irc_clients.emplace(hostname, std::make_shared(this->poller, hostname, nickname, username, - realname, this)); + realname, jid.domain, + this)); std::shared_ptr irc = this->irc_clients.at(hostname); return irc.get(); } diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index f6b8f9c..3161b85 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -26,9 +26,11 @@ using namespace std::chrono_literals; IrcClient::IrcClient(std::shared_ptr poller, const std::string& hostname, const std::string& nickname, const std::string& username, - const std::string& realname, Bridge* bridge): + const std::string& realname, const std::string& user_hostname, + Bridge* bridge): TCPSocketHandler(poller), hostname(hostname), + user_hostname(user_hostname), username(username), realname(realname), current_nick(nickname), @@ -113,6 +115,39 @@ void IrcClient::on_connection_failed(const std::string& reason) void IrcClient::on_connected() { + const auto webirc_password = Config::get("webirc_password", ""); + static std::string resolved_ip; + + if (!webirc_password.empty()) + { + if (!resolved_ip.empty()) + this->send_webirc_command(webirc_password, resolved_ip); + else + { // Start resolving the hostname of the user, and call + // on_connected again when it’s done + this->dns_resolver.resolve(this->user_hostname, "5222", + [this](const struct addrinfo* addr) + { + resolved_ip = addr_to_string(addr); + // Only continue the process if we + // didn’t get connected while we were + // resolving + if (this->is_connected()) + this->on_connected(); + }, + [this](const char* error_msg) + { + if (this->is_connected()) + { + this->on_connection_close("Could not resolve hostname "s + this->user_hostname + + ": " + error_msg); + this->send_quit_command(""); + } + }); + return; + } + } + #ifdef USE_DATABASE auto options = Database::get_irc_server_options(this->bridge->get_bare_jid(), this->get_hostname()); @@ -253,7 +288,7 @@ void IrcClient::send_raw(const std::string& txt) void IrcClient::send_user_command(const std::string& username, const std::string& realname) { - this->send_message(IrcMessage("USER", {username, "ignored", "ignored", realname})); + this->send_message(IrcMessage("USER", {username, this->user_hostname, "ignored", realname})); } void IrcClient::send_nick_command(const std::string& nick) @@ -266,6 +301,11 @@ void IrcClient::send_pass_command(const std::string& password) this->send_message(IrcMessage("PASS", {password})); } +void IrcClient::send_webirc_command(const std::string& password, const std::string& user_ip) +{ + this->send_message(IrcMessage("WEBIRC", {password, "biboumi", this->user_hostname, user_ip})); +} + void IrcClient::send_kick_command(const std::string& chan_name, const std::string& target, const std::string& reason) { this->send_message(IrcMessage("KICK", {chan_name, target, reason})); diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index 7a04164..885ec84 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -27,7 +28,8 @@ class IrcClient: public TCPSocketHandler public: explicit IrcClient(std::shared_ptr poller, const std::string& hostname, const std::string& nickname, const std::string& username, - const std::string& realname, Bridge* bridge); + const std::string& realname, const std::string& user_hostname, + Bridge* bridge); ~IrcClient(); /** * Connect to the IRC server @@ -88,6 +90,7 @@ public: */ void send_nick_command(const std::string& username); void send_pass_command(const std::string& password); + void send_webirc_command(const std::string& password, const std::string& user_ip); /** * Send the JOIN irc command. */ @@ -250,11 +253,18 @@ public: std::string get_nick() const { return this->current_nick; } bool is_welcomed() const { return this->welcomed; } + const Resolver& get_resolver() const; + private: /** * The hostname of the server we are connected to. */ const std::string hostname; + /** + * The hostname of the user. This is used in the USER and the WEBIRC + * commands, but only the one in WEBIRC will be used by the IRC server. + */ + const std::string user_hostname; /** * The username used in the USER irc command */ @@ -330,6 +340,11 @@ private: * A set of (lowercase) nicknames to which we sent a private message. */ std::set nicks_to_treat_as_private; + /** + * DNS resolver, used to resolve the hostname of the user if we are using + * the WebIRC protocole. + */ + Resolver dns_resolver; IrcClient(const IrcClient&) = delete; IrcClient(IrcClient&&) = delete; -- cgit v1.2.3