changeset 103:04d672ab41a4

Irccd: cleanup a lot server, #482
author David Demelier <markand@malikania.fr>
date Tue, 26 Apr 2016 19:31:42 +0200
parents 4777f7e18bf2
children be4b9ed19a17
files lib/irccd/cmd-server-connect.cpp lib/irccd/config.cpp lib/irccd/js-server.cpp lib/irccd/server-state-connected.cpp lib/irccd/server-state-connecting.cpp lib/irccd/server-state-disconnected.cpp lib/irccd/server.cpp lib/irccd/server.hpp
diffstat 8 files changed, 145 insertions(+), 109 deletions(-) [+]
line wrap: on
line diff
--- a/lib/irccd/cmd-server-connect.cpp	Tue Apr 26 13:19:35 2016 +0200
+++ b/lib/irccd/cmd-server-connect.cpp	Tue Apr 26 19:31:42 2016 +0200
@@ -106,8 +106,8 @@
 	ServerSettings settings;
 
 	settings.command = object.valueOr("commandChar", json::Type::String, settings.command).toString();
-	settings.recotries = object.valueOr("reconnectTries", json::Type::Int, settings.recotries).toInt();
-	settings.recotimeout = object.valueOr("reconnectTimeout", json::Type::Int, settings.recotimeout).toInt();
+	settings.reconnect_tries = object.valueOr("reconnectTries", json::Type::Int, settings.reconnect_tries).toInt();
+	settings.reconnect_timeout = object.valueOr("reconnectTimeout", json::Type::Int, settings.reconnect_timeout).toInt();
 
 	return settings;
 }
--- a/lib/irccd/config.cpp	Tue Apr 26 13:19:35 2016 +0200
+++ b/lib/irccd/config.cpp	Tue Apr 26 19:31:42 2016 +0200
@@ -300,9 +300,9 @@
 	/* Reconnect */
 	try {
 		if ((it = sc.find("reconnect-tries")) != sc.end())
-			settings.recotries = std::stoi(it->value());
+			settings.reconnect_tries = std::stoi(it->value());
 		if ((it = sc.find("reconnect-timeout")) != sc.end())
-			settings.recotimeout = std::stoi(it->value());
+			settings.reconnect_timeout = std::stoi(it->value());
 		if ((it = sc.find("ping-timeout")) != sc.end())
 			settings.ping_timeout = std::stoi(it->value());
 	} catch (const std::exception &) {
--- a/lib/irccd/js-server.cpp	Tue Apr 26 13:19:35 2016 +0200
+++ b/lib/irccd/js-server.cpp	Tue Apr 26 19:31:42 2016 +0200
@@ -388,8 +388,8 @@
 	for (const auto &chan: duk::getProperty<std::vector<std::string>>(ctx, 0, "channels"))
 		settings.channels.push_back(Server::splitChannel(chan));
 
-	settings.recotries = duk::optionalProperty<int>(ctx, 0, "recoTries", (int)settings.recotries);
-	settings.recotimeout = duk::optionalProperty<int>(ctx, 0, "recoTimeout", (int)settings.recotimeout);
+	settings.reconnect_tries = duk::optionalProperty<int>(ctx, 0, "recoTries", (int)settings.reconnect_tries);
+	settings.reconnect_timeout = duk::optionalProperty<int>(ctx, 0, "recoTimeout", (int)settings.reconnect_timeout);
 
 	if (duk::optionalProperty<bool>(ctx, 0, "joinInvite", false))
 		settings.flags |= ServerSettings::JoinInvite;
--- a/lib/irccd/server-state-connected.cpp	Tue Apr 26 13:19:35 2016 +0200
+++ b/lib/irccd/server-state-connected.cpp	Tue Apr 26 19:31:42 2016 +0200
@@ -33,15 +33,15 @@
 	if (!irc_is_connected(server.session())) {
 		log::warning() << "server " << info.name << ": disconnected" << std::endl;
 
-		if (settings.recotimeout > 0) {
+		if (settings.reconnect_timeout > 0) {
 			log::warning() << "server " << info.name << ": retrying in "
-				       << settings.recotimeout << " seconds" << std::endl;
+				       << settings.reconnect_timeout << " seconds" << std::endl;
 		}
 
 		server.next(std::make_unique<state::Disconnected>());
-	} else if (server.pingTimer().elapsed() >= settings.ping_timeout * 1000) {
+	} else if (server.cache().ping_timer.elapsed() >= settings.ping_timeout * 1000) {
 		log::warning() << "server " << info.name << ": ping timeout after "
-			       << (server.pingTimer().elapsed() / 1000) << " seconds" << std::endl;
+			       << (server.cache().ping_timer.elapsed() / 1000) << " seconds" << std::endl;
 		server.next(std::make_unique<state::Disconnected>());
 	} else {
 		irc_add_select_descriptors(server.session(), &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
--- a/lib/irccd/server-state-connecting.cpp	Tue Apr 26 13:19:35 2016 +0200
+++ b/lib/irccd/server-state-connecting.cpp	Tue Apr 26 19:31:42 2016 +0200
@@ -87,15 +87,16 @@
 	if (m_started) {
 		const ServerSettings &settings = server.settings();
 
-		if (m_timer.elapsed() > static_cast<unsigned>(settings.recotimeout * 1000)) {
+		if (m_timer.elapsed() > static_cast<unsigned>(settings.reconnect_timeout * 1000)) {
 			log::warning() << "server " << info.name << ": timeout while connecting" << std::endl;
 			server.next(std::make_unique<state::Disconnected>());
 		} else if (!irc_is_connected(server.session())) {
 			log::warning() << "server " << info.name << ": error while connecting: ";
 			log::warning() << irc_strerror(irc_errno(server.session())) << std::endl;
 
-			if (settings.recotries != 0)
-				log::warning() << "server " << info.name << ": retrying in " << settings.recotimeout << " seconds" << std::endl;
+			if (settings.reconnect_tries != 0)
+				log::warning() << "server " << info.name << ": retrying in "
+					       << settings.reconnect_timeout << " seconds" << std::endl;
 
 			server.next(std::make_unique<state::Disconnected>());
 		} else {
--- a/lib/irccd/server-state-disconnected.cpp	Tue Apr 26 13:19:35 2016 +0200
+++ b/lib/irccd/server-state-disconnected.cpp	Tue Apr 26 19:31:42 2016 +0200
@@ -29,18 +29,19 @@
 {
 	const ServerInfo &info = server.info();
 	ServerSettings &settings = server.settings();
+	ServerCache &cache = server.cache();
 
-	if (settings.recotries == 0) {
+	if (settings.reconnect_tries == 0) {
 		log::warning() << "server " << info.name << ": reconnection disabled, skipping" << std::endl;
 		server.onDie();
-	} else if (settings.recotries > 0 && settings.recocurrent > settings.recotries) {
+	} else if (settings.reconnect_tries > 0 && cache.reconnect_current > settings.reconnect_tries) {
 		log::warning() << "server " << info.name << ": giving up" << std::endl;
 		server.onDie();
 	} else {
-		if (m_timer.elapsed() > static_cast<unsigned>(settings.recotimeout * 1000)) {
+		if (m_timer.elapsed() > static_cast<unsigned>(settings.reconnect_timeout * 1000)) {
 			irc_disconnect(server.session());
 
-			settings.recocurrent ++;
+			server.cache().reconnect_current ++;
 			server.next(std::make_unique<state::Connecting>());
 		}
 	}
--- a/lib/irccd/server.cpp	Tue Apr 26 13:19:35 2016 +0200
+++ b/lib/irccd/server.cpp	Tue Apr 26 19:31:42 2016 +0200
@@ -31,66 +31,91 @@
 
 namespace irccd {
 
-bool Server::isSelf(const std::string &nick) const noexcept
-{
-	char target[32]{0};
+namespace {
 
-	irc_target_get_nick(nick.c_str(), target, sizeof (target));
-
-	return m_identity.nickname == target;
+/*
+ * strify
+ * ------------------------------------------------------------------
+ *
+ * Make sure to build a C++ string with a not-null C string.
+ */
+inline std::string strify(const char *s)
+{
+	return (s == nullptr) ? "" : std::string(s);
 }
 
-void Server::extractPrefixes(const std::string &line)
+/*
+ * cleanPrefix
+ * ------------------------------------------------------------------
+ *
+ * Remove the user prefix only if it is present in the mode table, for example removes @ from @irccd if and only if
+ * @ is a character mode (e.g. operator).
+ */
+std::string cleanPrefix(const ServerInfo &info, std::string nickname)
+{
+	if (nickname.length() > 0) {
+		for (const auto &pair : info.modes) {
+			if (nickname[0] == pair.second) {
+				nickname.erase(0, 1);
+			}
+		}
+	}
+
+	return nickname;
+}
+
+/*
+ * extractPrefixes
+ * ------------------------------------------------------------------
+ *
+ * Read modes from the IRC event numeric.
+ */
+std::map<ServerChanMode, char> extractPrefixes(const std::string &line)
 {
 	std::pair<char, char> table[16];
 	std::string buf = line.substr(7);
+	std::map<ServerChanMode, char> modes;
 
 	for (int i = 0; i < 16; ++i)
 		table[i] = std::make_pair(-1, -1);
 
 	int j = 0;
-	bool readModes = true;
+	bool read_modes = true;
 	for (size_t i = 0; i < buf.size(); ++i) {
 		if (buf[i] == '(')
 			continue;
 		if (buf[i] == ')') {
 			j = 0;
-			readModes = false;
+			read_modes = false;
 			continue;
 		}
 
-		if (readModes)
+		if (read_modes)
 			table[j++].first = buf[i];
 		else
 			table[j++].second = buf[i];
 	}
 
-	// Put these as a map of mode to prefix
+	/* Put these as a map of mode to prefix */
 	for (int i = 0; i < 16; ++i) {
 		auto key = static_cast<ServerChanMode>(table[i].first);
 		auto value = table[i].second;
 
-		m_info.modes.emplace(key, value);
+		modes.emplace(key, value);
 	}
+
+	return modes;
 }
 
-std::string Server::cleanPrefix(std::string nickname) const noexcept
-{
-	if (nickname.length() > 0)
-		for (const auto &pair : m_info.modes)
-			if (nickname[0] == pair.second)
-				nickname.erase(0, 1);
-
-	return nickname;
-}
+} // !namespace
 
 void Server::handleConnect(const char *, const char **) noexcept
 {
 	/* Reset the number of tried reconnection. */
-	m_settings.recocurrent = 0;
+	m_cache.reconnect_current = 0;
 
 	/* Reset the timer. */
-	m_ping_timer.reset();
+	m_cache.ping_timer.reset();
 
 	/* Don't forget to change state and notify. */
 	next(std::make_unique<state::Connected>());
@@ -193,7 +218,7 @@
 
 		/* The listing may add some prefixes, remove them if needed */
 		for (std::string u : users)
-			m_namesMap[params[2]].insert(cleanPrefix(u));
+			m_cache.names_map[params[2]].insert(cleanPrefix(m_info, u));
 	} else if (event == LIBIRC_RFC_RPL_ENDOFNAMES) {
 		/*
 		 * Called when end of name listing has finished on a channel.
@@ -206,12 +231,12 @@
 		if (c < 3 || params[1] == nullptr)
 			return;
 
-		auto it = m_namesMap.find(params[1]);
-		if (it != m_namesMap.end()) {
+		auto it = m_cache.names_map.find(params[1]);
+		if (it != m_cache.names_map.end()) {
 			onNames(params[1], it->second);
 
 			/* Don't forget to remove the list */
-			m_namesMap.erase(it);
+			m_cache.names_map.erase(it);
 		}
 	} else if (event == LIBIRC_RFC_RPL_WHOISUSER) {
 		/*
@@ -234,7 +259,7 @@
 		info.host = strify(params[3]);
 		info.realname = strify(params[5]);
 
-		m_whoisMap.emplace(info.nick, info);
+		m_cache.whois_map.emplace(info.nick, info);
 	} else if (event == LIBIRC_RFC_RPL_WHOISCHANNELS) {
 		/*
 		 * Called when we have received channels for one user.
@@ -246,13 +271,13 @@
 		if (c < 3 || !params[1] || !params[2])
 			return;
 
-		auto it = m_whoisMap.find(params[1]);
-		if (it != m_whoisMap.end()) {
+		auto it = m_cache.whois_map.find(params[1]);
+		if (it != m_cache.whois_map.end()) {
 			std::vector<std::string> channels = util::split(params[2], " \t");
 
 			/* Clean their prefixes */
 			for (auto &s : channels)
-				s = cleanPrefix(s);
+				s = cleanPrefix(m_info, s);
 
 			/* Insert */
 			it->second.channels = std::move(channels);
@@ -266,12 +291,12 @@
 		 * params[2] == End of WHOIS list
 		 */
 
-		auto it = m_whoisMap.find(params[1]);
-		if (it != m_whoisMap.end()) {
+		auto it = m_cache.whois_map.find(params[1]);
+		if (it != m_cache.whois_map.end()) {
 			onWhois(it->second);
 
 			/* Don't forget to remove */
-			m_whoisMap.erase(it);
+			m_cache.whois_map.erase(it);
 		}
 	} else if (event == /* RPL_BOUNCE */ 5) {
 		/*
@@ -279,7 +304,7 @@
 		 */
 		for (unsigned int i = 0; i < c; ++i) {
 			if (strncmp(params[i], "PREFIX", 6) == 0) {
-				extractPrefixes(params[i]);
+				m_info.modes = extractPrefixes(params[i]);
 				break;
 			}
 		}
@@ -294,7 +319,7 @@
 void Server::handlePing(const char *, const char **params) noexcept
 {
 	/* Reset the timer to detect disconnection. */
-	m_ping_timer.reset();
+	m_cache.ping_timer.reset();
 
 	/* Don't forget to respond */
 	send(params[0]);
@@ -448,6 +473,15 @@
 	irc_process_select_descriptors(*m_session, &setinput, &setoutput);
 }
 
+bool Server::isSelf(const std::string &nick) const noexcept
+{
+	char target[32]{0};
+
+	irc_target_get_nick(nick.c_str(), target, sizeof (target));
+
+	return m_identity.nickname == target;
+}
+
 void Server::cmode(std::string channel, std::string mode)
 {
 	m_queue.push([=] () {
@@ -455,21 +489,21 @@
 	});
 }
 
-void Server::cnotice(std::string channel, std::string message) noexcept
+void Server::cnotice(std::string channel, std::string message)
 {
 	m_queue.push([=] () {
 		return irc_cmd_notice(*m_session, channel.c_str(), message.c_str()) == 0;
 	});
 }
 
-void Server::invite(std::string target, std::string channel) noexcept
+void Server::invite(std::string target, std::string channel)
 {
 	m_queue.push([=] () {
 		return irc_cmd_invite(*m_session, target.c_str(), channel.c_str()) == 0;
 	});
 }
 
-void Server::join(std::string channel, std::string password) noexcept
+void Server::join(std::string channel, std::string password)
 {
 	m_queue.push([=] () {
 		const char *ptr = password.empty() ? nullptr : password.c_str();
@@ -478,7 +512,7 @@
 	});
 }
 
-void Server::kick(std::string target, std::string channel, std::string reason) noexcept
+void Server::kick(std::string target, std::string channel, std::string reason)
 {
 	m_queue.push([=] () {
 		return irc_cmd_kick(*m_session, target.c_str(), channel.c_str(), reason.c_str()) == 0;
--- a/lib/irccd/server.hpp	Tue Apr 26 13:19:35 2016 +0200
+++ b/lib/irccd/server.hpp	Tue Apr 26 19:31:42 2016 +0200
@@ -136,14 +136,36 @@
 
 	ServerChannels channels;		//!< List of channel to join
 	std::string command{"!"};		//!< The command character to trigger plugin command
-	std::int8_t recotries{-1};		//!< Number of tries to reconnect before giving up
-	std::uint16_t recotimeout{30};		//!< Number of seconds to wait before trying to connect
+	std::int8_t reconnect_tries{-1};		//!< Number of tries to reconnect before giving up
+	std::uint16_t reconnect_timeout{30};		//!< Number of seconds to wait before trying to connect
 	std::uint8_t flags{0};			//!< Optional flags
+	std::uint16_t ping_timeout{300};	//!< Time in seconds before ping timeout is announced
+};
 
-	std::uint16_t ping_timeout{300};	//!< Ping timeout in milliseconds
+/**
+ * \brief Some variables that are needed in many places internally.
+ */
+class ServerCache {
+public:
+	/**
+	 * Track elapsed time for ping timeout.
+	 */
+	ElapsedTimer ping_timer;
 
-	/* Private */
-	std::int8_t recocurrent{1};		//!< number of tries tested
+	/**
+	 * Number of reconnection already tested.
+	 */
+	std::int8_t reconnect_current{1};
+
+	/**
+	 * Map of names being build by channels.
+	 */
+	std::map<std::string, std::set<std::string>> names_map;
+
+	/**
+	 * Map of whois being build by nicknames.
+	 */
+	std::map<std::string, ServerWhois> whois_map;
 };
 
 /**
@@ -387,20 +409,11 @@
 	using SessionPtr = std::unique_ptr<Session>;
 	using Queue = std::queue<ServerCommand>;
 
-	/**
-	 * List of NAMES being built.
-	 */
-	using NamesMap = std::unordered_map<std::string, std::set<std::string>>;
-
-	/**
-	 * List of WHOIS being built.
-	 */
-	using WhoisMap	= std::unordered_map<std::string, ServerWhois>;
-
 private:
 	ServerInfo m_info;
 	ServerSettings m_settings;
 	ServerIdentity m_identity;
+	ServerCache m_cache;
 	SessionPtr m_session;
 	Queue m_queue;
 
@@ -408,28 +421,6 @@
 	std::unique_ptr<ServerState> m_state;
 	std::unique_ptr<ServerState> m_state_next;
 
-	/*
-	 * Detect timeout from server.
-	 */
-	ElapsedTimer m_ping_timer;
-
-	/*
-	 * The names map is being built by a successive call to handleNumeric so we need to store a temporary
-	 * map by channels to list of names. Then, when we receive the end of names listing, we remove the
-	 * temporary set of names and calls the appropriate signal.
-	 */
-	NamesMap m_namesMap;
-	WhoisMap m_whoisMap;
-
-	bool isSelf(const std::string &nick) const noexcept;
-	void extractPrefixes(const std::string &line);
-	std::string cleanPrefix(std::string nickname) const noexcept;
-
-	inline std::string strify(const char *s)
-	{
-		return (s == nullptr) ? "" : std::string(s);
-	}
-
 	void handleChannel(const char *, const char **) noexcept;
 	void handleChannelMode(const char *, const char **) noexcept;
 	void handleChannelNotice(const char *, const char **) noexcept;
@@ -475,22 +466,12 @@
 	 *
 	 * \param state the new state
 	 */
-	inline void next(std::unique_ptr<ServerState> state)
+	inline void next(std::unique_ptr<ServerState> state) noexcept
 	{
 		m_state_next = std::move(state);
 	}
 
 	/**
-	 * Get the ping timer.
-	 *
-	 * \return the ping timer
-	 */
-	inline ElapsedTimer &pingTimer() noexcept
-	{
-		return m_ping_timer;
-	}
-
-	/**
 	 * Switch to next state if it has.
 	 *
 	 * If the server is installed into irccd, it is called automatically.
@@ -573,6 +554,17 @@
 	}
 
 	/**
+	 * Access the cache.
+	 *
+	 * \return the cache
+	 * \warning use with care
+	 */
+	inline ServerCache &cache() noexcept
+	{
+		return m_cache;
+	}
+
+	/**
 	 * Get the server settings.
 	 *
 	 * \warning This overload should not be used by the user, it is required to
@@ -625,6 +617,14 @@
 	}
 
 	/**
+	 * Determine if the nickname is the bot itself.
+	 *
+	 * \param nick the nickname to check
+	 * \return true if it is the bot
+	 */
+	bool isSelf(const std::string &nick) const noexcept;
+
+	/**
 	 * Change the channel mode.
 	 *
 	 * \param channel the channel
@@ -640,7 +640,7 @@
 	 * \param message message notice
 	 * \note Thread-safe
 	 */
-	void cnotice(std::string channel, std::string message) noexcept;
+	void cnotice(std::string channel, std::string message);
 
 	/**
 	 * Invite a user to a channel.
@@ -649,7 +649,7 @@
 	 * \param channel the channel
 	 * \note Thread-safe
 	 */
-	void invite(std::string target, std::string channel) noexcept;
+	void invite(std::string target, std::string channel);
 
 	/**
 	 * Join a channel, the password is optional and can be kept empty.
@@ -658,7 +658,7 @@
 	 * \param password the optional password
 	 * \note Thread-safe
 	 */
-	void join(std::string channel, std::string password = "") noexcept;
+	void join(std::string channel, std::string password = "");
 
 	/**
 	 * Kick someone from the channel. Please be sure to have the rights
@@ -669,7 +669,7 @@
 	 * \param reason the optional reason
 	 * \note Thread-safe
 	 */
-	void kick(std::string target, std::string channel, std::string reason = "") noexcept;
+	void kick(std::string target, std::string channel, std::string reason = "");
 
 	/**
 	 * Send a CTCP Action as known as /me. The target may be either a