Mercurial > irccd
changeset 550:64cb98dd8e9d
Irccd: finish custom IRC library, closes #581
Finally remove the lovely libircclient that has been used since the
beginning of irccd back in 2013.
Several issues made the choice of developing a custom IRC library:
- libircclient requires select(2) system call and works in procedural
paradigm which is incompatible with our Boost.Asio main loop,
- there was no way in the libircclient to detect custom message like
PING to handle dead servers, this required to bundle the library
within the application,
- the library use static output buffer which requires to keep an
internal queue of commands that we flush at each iteration.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 24 Nov 2017 20:05:15 +0100 |
parents | 68032209609d |
children | a5e1c91abb8e |
files | CREDITS.md libirccd-js/irccd/js/server_jsapi.cpp libirccd-js/irccd/js/util_jsapi.cpp libirccd/irccd/command.cpp libirccd/irccd/irc.cpp libirccd/irccd/irc.hpp libirccd/irccd/irccd.cpp libirccd/irccd/server.cpp libirccd/irccd/server.hpp libirccd/irccd/service.cpp libirccd/irccd/service.hpp tests/irc/main.cpp tests/js-util/main.cpp |
diffstat | 13 files changed, 116 insertions(+), 113 deletions(-) [+] |
line wrap: on
line diff
--- a/CREDITS.md Thu Nov 23 22:45:12 2017 +0100 +++ b/CREDITS.md Fri Nov 24 20:05:15 2017 +0100 @@ -5,7 +5,8 @@ ---------------------- - libircclient, http://www.ulduzsoft.com/linux/libircclient - Very powerful and great C IRC library. + Very powerful and great C IRC library used until version 3.0.0, has been a + source of inspiration. - OpenSSL, http://openssl.org Free, open-source famous crypto library.
--- a/libirccd-js/irccd/js/server_jsapi.cpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd-js/irccd/js/server_jsapi.cpp Fri Nov 24 20:05:15 2017 +0100 @@ -392,7 +392,7 @@ try { auto json = duk_json_encode(ctx, 0); - auto s = server::from_json(nlohmann::json::parse(json)); + auto s = server::from_json(dukx_get_irccd(ctx).service(), nlohmann::json::parse(json)); duk_push_this(ctx); duk_push_pointer(ctx, new std::shared_ptr<server>(std::move(s)));
--- a/libirccd-js/irccd/js/util_jsapi.cpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd-js/irccd/js/util_jsapi.cpp Fri Nov 24 20:05:15 2017 +0100 @@ -238,13 +238,7 @@ */ duk_ret_t splituser(duk_context* ctx) { - auto target = duk_require_string(ctx, 0); - char nick[32] = {0}; - -#if 0 - irc_target_get_nick(target, nick, sizeof (nick) -1); -#endif - duk_push_string(ctx, nick); + dukx_push_std_string(ctx, irc::user::parse(duk_require_string(ctx, 0)).nick()); return 1; } @@ -262,13 +256,7 @@ */ duk_ret_t splithost(duk_context* ctx) { - auto target = duk_require_string(ctx, 0); - char host[32] = {0}; - -#if 0 - irc_target_get_host(target, host, sizeof (host) -1); -#endif - duk_push_string(ctx, host); + dukx_push_std_string(ctx, irc::user::parse(duk_require_string(ctx, 0)).host()); return 1; }
--- a/libirccd/irccd/command.cpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd/irccd/command.cpp Fri Nov 24 20:05:15 2017 +0100 @@ -257,7 +257,7 @@ void server_connect_command::exec(irccd& irccd, transport_client& client, const nlohmann::json& args) { - auto server = server::from_json(args); + auto server = server::from_json(irccd.service(), args); if (irccd.servers().has(server->name())) client.error("server-connect", "server already exists");
--- a/libirccd/irccd/irc.cpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd/irccd/irc.cpp Fri Nov 24 20:05:15 2017 +0100 @@ -16,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <cassert> #include <iterator> #include <sstream> @@ -103,12 +104,32 @@ return {std::move(prefix), std::move(command), std::move(args)}; } +bool message::is_ctcp(unsigned index) const noexcept +{ + auto a = arg(index); + + if (a.empty()) + return false; + + return a.front() == 0x01 && a.back() == 0x01; +} + +std::string message::ctcp(unsigned index) const +{ + assert(is_ctcp(index)); + + return args_[index].substr(1, args_[index].size() - 1); +} + user user::parse(const std::string& line) { + if (line.empty()) + return {"", ""}; + auto pos = line.find("!"); if (pos == std::string::npos) - return {"", ""}; + return {line, ""}; return {line.substr(0, pos), line.substr(pos + 1)}; }
--- a/libirccd/irccd/irc.hpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd/irccd/irc.hpp Fri Nov 24 20:05:15 2017 +0100 @@ -276,6 +276,23 @@ } /** + * Tells if the message is a CTCP. + * + * \param index the param index (maybe out of bounds) + * \return true if CTCP + */ + bool is_ctcp(unsigned index) const noexcept; + + /** + * Parse a CTCP message. + * + * \pre is_ctcp(index) + * \param index the param index + * \return the CTCP command + */ + std::string ctcp(unsigned index) const; + + /** * Parse a IRC message. * * \param line the buffer content (without \r\n) @@ -489,7 +506,8 @@ template <typename Socket> void basic_connection<Socket>::do_send(const std::string& message, send_t handler) { - boost::asio::async_write(socket_, boost::asio::buffer(message), [handler, message] (auto code, auto xfer) { + boost::asio::async_write(socket_, boost::asio::buffer(message), [handler, message] (auto code, auto) { + // TODO: xfer handler(code); }); }
--- a/libirccd/irccd/irccd.cpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd/irccd/irccd.cpp Fri Nov 24 20:05:15 2017 +0100 @@ -49,19 +49,24 @@ void irccd::run() { + for (;;) + service_.run(); + +#if 0 while (running_) { net_util::poll(100, *this); service_.poll(); } +#endif } -void irccd::prepare(fd_set& in, fd_set& out, net::Handle& max) +void irccd::prepare(fd_set&, fd_set&, net::Handle&) { - net_util::prepare(in, out, max, *itr_service_, *server_service_); } -void irccd::sync(fd_set& in, fd_set& out) +void irccd::sync(fd_set& , fd_set& ) { +#if 0 if (!running_) return; @@ -89,6 +94,7 @@ for (auto& ev : copy) ev(*this); +#endif } void irccd::stop()
--- a/libirccd/irccd/server.cpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd/irccd/server.cpp Fri Nov 24 20:05:15 2017 +0100 @@ -77,6 +77,7 @@ int j = 0; bool read_modes = true; + for (size_t i = 0; i < buf.size(); ++i) { if (buf[i] == '(') continue; @@ -110,24 +111,9 @@ jchannels_.erase(std::remove(jchannels_.begin(), jchannels_.end(), channel), jchannels_.end()); } -#if 0 - -void server::handle_channel(const char* orig, const char** params) noexcept -{ - on_message({shared_from_this(), strify(orig), strify(params[0]), strify(params[1])}); -} - -void server::handle_ctcp_action(const char* orig, const char** params) noexcept +std::shared_ptr<server> server::from_json(boost::asio::io_service& service, const nlohmann::json& object) { - on_me({shared_from_this(), strify(orig), strify(params[0]), strify(params[1])}); -} - -#endif - -std::shared_ptr<server> server::from_json(const nlohmann::json& object) -{ -#if 0 - auto sv = std::make_shared<server>(json_util::require_identifier(object, "name")); + auto sv = std::make_shared<server>(service, json_util::require_identifier(object, "name")); sv->set_host(json_util::require_string(object, "host")); sv->set_password(json_util::get_string(object, "password")); @@ -153,8 +139,6 @@ sv->set_flags(sv->flags() | server::join_invite); return sv; -#endif - return nullptr; } channel server::split_channel(const std::string& value) @@ -178,16 +162,6 @@ username_ = user.empty() ? "irccd" : user; } -void server::dispatch_channel_mode(const irc::message& msg) -{ - on_channel_mode({ shared_from_this(), msg.arg(0), msg.arg(1), msg.arg(2), msg.arg(3)}); -} - -void server::dispatch_channel_notice(const irc::message& msg) -{ - on_channel_notice({shared_from_this(), msg.arg(0), msg.arg(0), msg.arg(1)}); -} - void server::dispatch_connect(const irc::message&) { recocur_ = 1; @@ -241,6 +215,7 @@ * params[2] == End of WHOIS list */ auto it = whois_map_.find(msg.arg(1)); + if (it != whois_map_.end()) { on_whois({shared_from_this(), it->second}); @@ -251,11 +226,11 @@ void server::dispatch_invite(const irc::message& msg) { - // If joininvite is set, join the channel. - if ((flags_ & join_invite) && is_self(msg.arg(1))) - join(msg.arg(2)); + // If join-invite is set, join the channel. + if ((flags_ & join_invite) && is_self(msg.arg(0))) + join(msg.arg(1)); - on_invite({shared_from_this(), msg.arg(0), msg.arg(2), msg.arg(1)}); + on_invite({shared_from_this(), msg.prefix(), msg.arg(1), msg.arg(0)}); } void server::dispatch_isupport(const irc::message& msg) @@ -283,36 +258,36 @@ break; } } - } void server::dispatch_join(const irc::message& msg) { - if (is_self(msg.arg(0))) - jchannels_.push_back(msg.arg(1)); + if (is_self(msg.prefix())) + jchannels_.push_back(msg.arg(0)); - on_join({shared_from_this(), msg.arg(0), msg.arg(1)}); + on_join({shared_from_this(), msg.prefix(), msg.arg(0)}); } void server::dispatch_kick(const irc::message& msg) { - if (is_self(msg.arg(2))) { + if (is_self(msg.arg(1))) { // Remove the channel from the joined list. - remove_joined_channel(msg.arg(1)); + remove_joined_channel(msg.arg(0)); // Rejoin the channel if the option has been set and I was kicked. if (flags_ & auto_rejoin) - join(msg.arg(1)); + join(msg.arg(0)); } - on_kick({ shared_from_this(), msg.arg(0), msg.arg(1), msg.arg(2), msg.arg(3)}); + on_kick({shared_from_this(), msg.prefix(), msg.arg(0), msg.arg(1), msg.arg(2)}); } void server::dispatch_mode(const irc::message& msg) { -#if 0 - on_mode({shared_from_this(), msg.arg(0), msg.arg(1)}); -#endif + if (is_self(msg.arg(1))) + on_mode({shared_from_this(), msg.prefix(), msg.arg(1)}); + else + on_channel_mode({shared_from_this(), msg.prefix(), msg.arg(0), msg.arg(1), msg.arg(2)}); } void server::dispatch_namreply(const irc::message& msg) @@ -341,31 +316,49 @@ void server::dispatch_nick(const irc::message& msg) { // Update our nickname. - if (is_self(msg.arg(0))) + if (is_self(msg.prefix())) nickname_ = msg.arg(0); - on_nick({shared_from_this(), msg.arg(0), msg.arg(1)}); + on_nick({shared_from_this(), msg.prefix(), msg.arg(0)}); } void server::dispatch_notice(const irc::message& msg) { - on_notice({shared_from_this(), msg.arg(0), msg.arg(2)}); + if (is_self(msg.arg(1))) + on_notice({shared_from_this(), msg.prefix(), msg.arg(1)}); + else + on_channel_notice({shared_from_this(), msg.prefix(), msg.arg(0), msg.arg(1)}); } void server::dispatch_part(const irc::message& msg) { // Remove the channel from the joined list if I left a channel. - if (is_self(msg.arg(0))) + if (is_self(msg.prefix())) remove_joined_channel(msg.arg(1)); - on_part({shared_from_this(), msg.arg(0), msg.arg(1), msg.arg(2)}); + on_part({shared_from_this(), msg.prefix(), msg.arg(0), msg.arg(1)}); } void server::dispatch_ping(const irc::message& msg) { assert(msg.command() == "PING"); - conn_->send(string_util::sprintf("PONG %s", msg.arg(1))); + conn_->send(string_util::sprintf("PONG %s", msg.arg(0))); +} + +void server::dispatch_privmsg(const irc::message& msg) +{ + assert(msg.command() == "PRIVMSG"); + + if (msg.is_ctcp(1)) { + auto cmd = msg.ctcp(1); + + if (cmd.compare(0, 6, "ACTION") == 0) + on_me({shared_from_this(), msg.prefix(), msg.arg(0), cmd.substr(7)}); + } else if (is_self(msg.arg(0))) + on_query({shared_from_this(), msg.prefix(), msg.arg(1)}); + else + on_message({shared_from_this(), msg.prefix(), msg.arg(0), msg.arg(1)}); } void server::dispatch_topic(const irc::message& msg) @@ -430,13 +423,15 @@ if (message.is(5)) dispatch_isupport(message); else if (message.is(irc::err::nomotd) || message.is(irc::rpl::endofmotd)) - dispatch_connect(message); + dispatch_connect(message); else if (message.command() == "INVITE") dispatch_invite(message); else if (message.command() == "JOIN") dispatch_join(message); else if (message.command() == "KICK") dispatch_kick(message); + else if (message.command() == "MODE") + dispatch_mode(message); else if (message.command() == "NICK") dispatch_nick(message); else if (message.command() == "NOTICE") @@ -447,6 +442,8 @@ dispatch_part(message); else if (message.command() == "PING") dispatch_ping(message); + else if (message.command() == "PRIVMSG") + dispatch_privmsg(message); else if (message.is(irc::rpl::namreply)) dispatch_namreply(message); else if (message.is(irc::rpl::endofnames))
--- a/libirccd/irccd/server.hpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd/irccd/server.hpp Fri Nov 24 20:05:15 2017 +0100 @@ -476,6 +476,7 @@ void dispatch_notice(const irc::message&); void dispatch_part(const irc::message&); void dispatch_ping(const irc::message&); + void dispatch_privmsg(const irc::message&); void dispatch_topic(const irc::message&); void dispatch_whoischannels(const irc::message&); void dispatch_whoisuser(const irc::message&); @@ -492,11 +493,12 @@ * * Used in JavaScript API and transport commands. * + * \param service the io service * \param object the object * \return the server * \throw std::exception on failures */ - static std::shared_ptr<server> from_json(const nlohmann::json& object); + static std::shared_ptr<server> from_json(boost::asio::io_service& service, const nlohmann::json& object); /** * Split a channel from the form channel:password into a server_channel
--- a/libirccd/irccd/service.cpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd/irccd/service.cpp Fri Nov 24 20:05:15 2017 +0100 @@ -793,7 +793,7 @@ log::debug() << " username: " << ev.whois.user << "\n"; log::debug() << " host: " << ev.whois.host << "\n"; log::debug() << " realname: " << ev.whois.realname << "\n"; - log::debug() << " channels: " << string_util::join(ev.whois.channels.begin(), ev.whois.channels.end()) << std::endl; + log::debug() << " channels: " << string_util::join(ev.whois.channels.begin(), ev.whois.channels.end(), ", ") << std::endl; irccd_.transports().broadcast(nlohmann::json::object({ { "event", "onWhois" }, @@ -819,24 +819,6 @@ { } -void server_service::prepare(fd_set& in, fd_set& out, net::Handle& max) -{ -#if 0 - for (auto& server : servers_) { - server->update(); - server->prepare(in, out, max); - } -#endif -} - -void server_service::sync(fd_set& in, fd_set& out) -{ -#if 0 - for (auto& server : servers_) - server->sync(in, out); -#endif -} - bool server_service::has(const std::string& name) const noexcept { return std::count_if(servers_.cbegin(), servers_.end(), [&] (const auto& server) {
--- a/libirccd/irccd/service.hpp Thu Nov 23 22:45:12 2017 +0100 +++ b/libirccd/irccd/service.hpp Fri Nov 24 20:05:15 2017 +0100 @@ -404,16 +404,6 @@ server_service(irccd& instance); /** - * \copydoc Service::prepare - */ - void prepare(fd_set& in, fd_set& out, net::Handle& max); - - /** - * \copydoc Service::sync - */ - void sync(fd_set& in, fd_set& out); - - /** * Get the list of servers * * \return the servers
--- a/tests/irc/main.cpp Thu Nov 23 22:45:12 2017 +0100 +++ b/tests/irc/main.cpp Fri Nov 24 20:05:15 2017 +0100 @@ -65,18 +65,16 @@ BOOST_TEST(user.nick() == "jean"); BOOST_TEST(user.host() == "~jean@127.0.0.1"); + + auto usersimple = irc::user::parse("jean"); + + BOOST_TEST(usersimple.nick() == "jean"); + BOOST_TEST(usersimple.host().empty()); } -BOOST_AUTO_TEST_CASE(invalid) +BOOST_AUTO_TEST_CASE(empty) { - irc::user user("", ""); - - user = irc::user::parse("notavalidcombination"); - - BOOST_TEST(user.nick().empty()); - BOOST_TEST(user.host().empty()); - - user = irc::user::parse(""); + auto user = irc::user::parse(""); BOOST_TEST(user.nick().empty()); BOOST_TEST(user.host().empty());
--- a/tests/js-util/main.cpp Thu Nov 23 22:45:12 2017 +0100 +++ b/tests/js-util/main.cpp Fri Nov 24 20:05:15 2017 +0100 @@ -60,7 +60,7 @@ throw dukx_exception(plugin_->context(), -1); BOOST_TEST(duk_get_global_string(plugin_->context(), "result")); - BOOST_TEST(duk_get_string(plugin_->context(), -1) == "!~user@hyper/super/host"); + BOOST_TEST(duk_get_string(plugin_->context(), -1) == "~user@hyper/super/host"); } /*