mass reformat

This commit is contained in:
Eric Mertens 2025-01-25 15:45:31 -08:00
parent fd4612d385
commit 093515c3ec
17 changed files with 659 additions and 311 deletions

232
.clang-format Normal file
View File

@ -0,0 +1,232 @@
---
Language: Cpp
# BasedOnStyle: WebKit
AccessModifierOffset: -4
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: DontAlign
AlignTrailingComments:
Kind: Never
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterExternBlock: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: All
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Custom
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 0
CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros: []
IfMacros: []
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: ".*"
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: "(Test)?$"
IncludeIsMainSourceRegex: ""
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: LF
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Left
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: true
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros: []
StatementMacros: []
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros: []

View File

@ -24,23 +24,27 @@ auto Connection::write_buffers() -> void
{ {
std::vector<boost::asio::const_buffer> buffers; std::vector<boost::asio::const_buffer> buffers;
buffers.reserve(write_strings_.size()); buffers.reserve(write_strings_.size());
for (auto const& elt : write_strings_) for (const auto &elt : write_strings_)
{ {
buffers.push_back(boost::asio::buffer(elt)); buffers.push_back(boost::asio::buffer(elt));
} }
boost::asio::async_write( boost::asio::async_write(
stream_, stream_,
buffers, buffers,
[this, strings = std::move(write_strings_)](boost::system::error_code const& error, std::size_t) [this, strings = std::move(write_strings_)](const boost::system::error_code &error, std::size_t) {
if (not error)
{
if (write_strings_.empty())
{ {
if (not error) {
if (write_strings_.empty()) {
write_posted_ = false; write_posted_ = false;
} else { }
else
{
write_buffers(); write_buffers();
} }
} }
}); }
);
write_strings_.clear(); write_strings_.clear();
} }
@ -53,24 +57,23 @@ auto Connection::connect(
using namespace std::placeholders; using namespace std::placeholders;
// keep connection alive while coroutine is active // keep connection alive while coroutine is active
auto const self = shared_from_this(); const auto self = shared_from_this();
{ {
auto resolver = boost::asio::ip::tcp::resolver{io}; auto resolver = boost::asio::ip::tcp::resolver{io};
auto const endpoints = co_await resolver.async_resolve(host, port, boost::asio::use_awaitable); const auto endpoints = co_await resolver.async_resolve(host, port, boost::asio::use_awaitable);
auto const endpoint = co_await boost::asio::async_connect(stream_, endpoints, boost::asio::use_awaitable); const auto endpoint = co_await boost::asio::async_connect(stream_, endpoints, boost::asio::use_awaitable);
BOOST_LOG_TRIVIAL(debug) << "CONNECTED: " << endpoint;
BOOST_LOG_TRIVIAL(debug) << "CONNECTED: " << endpoint;
sig_connect(); sig_connect();
} }
// Start the queue writer after connection
watchdog(); watchdog();
for (LineBuffer buffer{32'768};;) for (LineBuffer buffer{32'768};;)
{ {
boost::system::error_code error; boost::system::error_code error;
auto const n = co_await stream_.async_read_some(buffer.get_buffer(), boost::asio::redirect_error(boost::asio::use_awaitable, error)); const auto n = co_await stream_.async_read_some(buffer.get_buffer(), boost::asio::redirect_error(boost::asio::use_awaitable, error));
if (error) if (error)
{ {
break; break;
@ -84,14 +87,15 @@ auto Connection::connect(
watchdog_timer_.cancel(); watchdog_timer_.cancel();
stream_.close(); stream_.close();
BOOST_LOG_TRIVIAL(debug) << "DISCONNECTED";
sig_disconnect(); sig_disconnect();
} }
auto Connection::watchdog() -> void auto Connection::watchdog() -> void
{ {
watchdog_timer_.expires_after(watchdog_duration); watchdog_timer_.expires_after(watchdog_duration);
watchdog_timer_.async_wait([this](auto const& error) watchdog_timer_.async_wait([this](const auto &error) {
{
if (not error) if (not error)
{ {
if (stalled_) if (stalled_)
@ -118,15 +122,17 @@ auto Connection::watchdog_activity() -> void
/// Parse IRC message line and dispatch it to the ircmsg slot. /// Parse IRC message line and dispatch it to the ircmsg slot.
auto Connection::dispatch_line(char *line) -> void auto Connection::dispatch_line(char *line) -> void
{ {
auto const msg = parse_irc_message(line); const auto msg = parse_irc_message(line);
auto const recognized = IrcCommandHash::in_word_set(msg.command.data(), msg.command.size()); const auto recognized = IrcCommandHash::in_word_set(msg.command.data(), msg.command.size());
auto const command const auto command
= recognized = recognized
&& recognized->min_args <= msg.args.size() && recognized->min_args <= msg.args.size()
&& recognized->max_args >= msg.args.size() && recognized->max_args >= msg.args.size()
? recognized->command : IrcCommand::UNKNOWN; ? recognized->command
: IrcCommand::UNKNOWN;
switch (command) { switch (command)
{
// Respond to pings immediate and discard // Respond to pings immediate and discard
case IrcCommand::PING: case IrcCommand::PING:
@ -145,7 +151,8 @@ auto Connection::dispatch_line(char *line) -> void
// Server notice generate snote events but not IRC command events // Server notice generate snote events but not IRC command events
case IrcCommand::NOTICE: case IrcCommand::NOTICE:
if (auto match = snoteCore.match(msg)) { if (auto match = snoteCore.match(msg))
{
sig_snote(*match); sig_snote(*match);
break; break;
} }
@ -156,7 +163,6 @@ auto Connection::dispatch_line(char *line) -> void
sig_ircmsg(command, msg); sig_ircmsg(command, msg);
break; break;
} }
} }
auto Connection::write_line(std::string message) -> void auto Connection::write_line(std::string message) -> void
@ -165,7 +171,8 @@ auto Connection::write_line(std::string message) -> void
message += "\r\n"; message += "\r\n";
write_strings_.push_back(std::move(message)); write_strings_.push_back(std::move(message));
if (not write_posted_) { if (not write_posted_)
{
write_posted_ = true; write_posted_ = true;
boost::asio::post(stream_.get_executor(), [weak = weak_from_this()]() { boost::asio::post(stream_.get_executor(), [weak = weak_from_this()]() {
if (auto self = weak.lock()) if (auto self = weak.lock())
@ -181,8 +188,7 @@ auto Connection::close() -> void
stream_.close(); stream_.close();
} }
static static auto is_invalid_last(char x) -> bool
auto is_invalid_last(char x) -> bool
{ {
return x == '\0' || x == '\r' || x == '\n'; return x == '\0' || x == '\r' || x == '\n';
} }
@ -261,7 +267,8 @@ auto Connection::send_authenticate(std::string_view message) -> void
auto Connection::on_authenticate(const std::string_view chunk) -> void auto Connection::on_authenticate(const std::string_view chunk) -> void
{ {
if (chunk != "+"sv) { if (chunk != "+"sv)
{
authenticate_buffer_ += chunk; authenticate_buffer_ += chunk;
} }
@ -275,7 +282,9 @@ auto Connection::on_authenticate(const std::string_view chunk) -> void
{ {
decoded.resize(len); decoded.resize(len);
sig_authenticate(decoded); sig_authenticate(decoded);
} else { }
else
{
BOOST_LOG_TRIVIAL(debug) << "Invalid AUTHENTICATE base64"sv; BOOST_LOG_TRIVIAL(debug) << "Invalid AUTHENTICATE base64"sv;
send_authenticate("*"sv); // abort SASL send_authenticate("*"sv); // abort SASL
} }
@ -300,7 +309,8 @@ auto Connection::send_authenticate_encoded(std::string_view body) -> void
std::string encoded(mybase64::encoded_size(body.size()), 0); std::string encoded(mybase64::encoded_size(body.size()), 0);
mybase64::encode(body, encoded.data()); mybase64::encode(body, encoded.data());
for (size_t lo = 0; lo < encoded.size(); lo += 400) { for (size_t lo = 0; lo < encoded.size(); lo += 400)
{
const auto hi = std::min(lo + 400, encoded.size()); const auto hi = std::min(lo + 400, encoded.size());
const std::string_view chunk{encoded.begin() + lo, encoded.begin() + hi}; const std::string_view chunk{encoded.begin() + lo, encoded.begin() + hi};
send_authenticate(chunk); send_authenticate(chunk);

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "ircmsg.hpp"
#include "irc_command.hpp" #include "irc_command.hpp"
#include "ircmsg.hpp"
#include "snote.hpp" #include "snote.hpp"
#include <boost/asio.hpp> #include <boost/asio.hpp>
@ -51,7 +51,8 @@ public:
boost::signals2::signal<void(SnoteMatch &)> sig_snote; boost::signals2::signal<void(SnoteMatch &)> sig_snote;
boost::signals2::signal<void(std::string_view)> sig_authenticate; boost::signals2::signal<void(std::string_view)> sig_authenticate;
auto get_executor() -> boost::asio::any_io_executor { auto get_executor() -> boost::asio::any_io_executor
{
return stream_.get_executor(); return stream_.get_executor();
} }
@ -83,8 +84,7 @@ public:
template <typename... Args> template <typename... Args>
auto Connection::write_irc(std::string front, std::string_view next, Args... rest) -> void auto Connection::write_irc(std::string front, std::string_view next, Args... rest) -> void
{ {
auto const is_invalid = [](char const x) -> bool const auto is_invalid = [](const char x) -> bool {
{
return x == '\0' || x == '\r' || x == '\n' || x == ' '; return x == '\0' || x == '\r' || x == '\n' || x == ' ';
}; };

View File

@ -1,21 +1,20 @@
#include "irc_coroutine.hpp" #include "irc_coroutine.hpp"
auto irc_coroutine::is_running() -> bool { auto irc_coroutine::is_running() -> bool
{
return promise().connection_ != nullptr; return promise().connection_ != nullptr;
} }
auto irc_coroutine::exception() -> std::exception_ptr { auto irc_coroutine::exception() -> std::exception_ptr
{
return promise().exception_; return promise().exception_;
} }
auto irc_coroutine::start(Connection& connection) -> void { auto irc_coroutine::start(Connection &connection) -> void
{
promise().connection_ = connection.shared_from_this(); promise().connection_ = connection.shared_from_this();
resume(); resume();
} }
void wait_ircmsg::stop() { void wait_ircmsg::stop() { ircmsg_slot_.disconnect(); }
ircmsg_slot_.disconnect();
}
void wait_timeout::stop() { void wait_timeout::stop() { timer_.reset(); }
timer_.reset();
}

View File

@ -11,7 +11,8 @@
struct irc_promise; struct irc_promise;
/// A coroutine that can co_await on various IRC events /// A coroutine that can co_await on various IRC events
struct irc_coroutine : std::coroutine_handle<irc_promise> { struct irc_coroutine : std::coroutine_handle<irc_promise>
{
using promise_type = irc_promise; using promise_type = irc_promise;
/// Start the coroutine and associate it with a specific connection. /// Start the coroutine and associate it with a specific connection.
@ -44,12 +45,14 @@ struct irc_promise
auto final_suspend() noexcept -> std::suspend_always { return {}; } auto final_suspend() noexcept -> std::suspend_always { return {}; }
// Normal termination // Normal termination
auto return_void() -> void { auto return_void() -> void
{
connection_.reset(); connection_.reset();
} }
// Abnormal termination - remember the exception // Abnormal termination - remember the exception
auto unhandled_exception() -> void { auto unhandled_exception() -> void
{
connection_.reset(); connection_.reset();
exception_ = std::current_exception(); exception_ = std::current_exception();
} }
@ -59,7 +62,8 @@ template<typename ... Ts>
class Wait; class Wait;
/// Argument to a Wait that expects one or more IRC messages /// Argument to a Wait that expects one or more IRC messages
class wait_ircmsg { class wait_ircmsg
{
// Vector of commands this wait is expecting. Leave empty to accept all messages. // Vector of commands this wait is expecting. Leave empty to accept all messages.
std::vector<IrcCommand> want_cmds_; std::vector<IrcCommand> want_cmds_;
@ -69,26 +73,38 @@ class wait_ircmsg {
public: public:
using result_type = std::pair<IrcCommand, const IrcMsg &>; using result_type = std::pair<IrcCommand, const IrcMsg &>;
wait_ircmsg(std::initializer_list<IrcCommand> want_cmds) : want_cmds_{want_cmds} {} wait_ircmsg(std::initializer_list<IrcCommand> want_cmds)
: want_cmds_{want_cmds}
{
}
template <size_t I, typename... Ts> auto start(Wait<Ts...>& command) -> void; template <size_t I, typename... Ts>
auto start(Wait<Ts...> &command) -> void;
auto stop() -> void; auto stop() -> void;
}; };
class wait_timeout { class wait_timeout
{
std::optional<boost::asio::steady_timer> timer_; std::optional<boost::asio::steady_timer> timer_;
std::chrono::milliseconds timeout_; std::chrono::milliseconds timeout_;
public: public:
struct result_type {}; struct result_type
wait_timeout(std::chrono::milliseconds timeout) : timeout_{timeout} {} {
};
wait_timeout(std::chrono::milliseconds timeout)
: timeout_{timeout}
{
}
template <size_t I, typename... Ts> auto start(Wait<Ts...>& command) -> void; template <size_t I, typename... Ts>
auto start(Wait<Ts...> &command) -> void;
auto stop() -> void; auto stop() -> void;
}; };
template <typename... Ts> template <typename... Ts>
class Wait { class Wait
{
// State associated with each wait mode // State associated with each wait mode
std::tuple<Ts...> modes_; std::tuple<Ts...> modes_;
@ -105,31 +121,39 @@ class Wait {
boost::signals2::scoped_connection disconnect_slot_; boost::signals2::scoped_connection disconnect_slot_;
template <size_t I> template <size_t I>
auto start_mode() -> void { auto start_mode() -> void
{
std::get<I>(modes_).template start<I, Ts...>(*this); std::get<I>(modes_).template start<I, Ts...>(*this);
} }
template <std::size_t... Indices> template <std::size_t... Indices>
auto start_modes(std::index_sequence<Indices...>) -> void { auto start_modes(std::index_sequence<Indices...>) -> void
{
(start_mode<Indices>(), ...); (start_mode<Indices>(), ...);
} }
template <std::size_t... Indices> template <std::size_t... Indices>
auto stop_modes(std::index_sequence<Indices...>) -> void { auto stop_modes(std::index_sequence<Indices...>) -> void
{
(std::get<Indices>(modes_).stop(), ...); (std::get<Indices>(modes_).stop(), ...);
} }
public: public:
Wait(Ts &&...modes) : modes_{std::forward<Ts>(modes)...} {} Wait(Ts &&...modes)
: modes_{std::forward<Ts>(modes)...}
{
}
// Get the connection that this coroutine was started with. // Get the connection that this coroutine was started with.
auto get_connection() const -> Connection & { auto get_connection() const -> Connection &
{
return *handle_.promise().connection_; return *handle_.promise().connection_;
} }
// Store a successful result and resume the coroutine // Store a successful result and resume the coroutine
template <size_t I, typename... Args> template <size_t I, typename... Args>
auto complete(Args &&...args) -> void { auto complete(Args &&...args) -> void
{
result_.emplace(std::in_place_index<I>, std::forward<Args>(args)...); result_.emplace(std::in_place_index<I>, std::forward<Args>(args)...);
handle_.resume(); handle_.resume();
} }
@ -148,10 +172,9 @@ template <size_t I, typename... Ts>
auto wait_ircmsg::start(Wait<Ts...> &command) -> void auto wait_ircmsg::start(Wait<Ts...> &command) -> void
{ {
ircmsg_slot_ = command.get_connection().sig_ircmsg.connect([this, &command](auto cmd, auto &msg) { ircmsg_slot_ = command.get_connection().sig_ircmsg.connect([this, &command](auto cmd, auto &msg) {
auto const wanted = const auto wanted = want_cmds_.empty() || std::find(want_cmds_.begin(), want_cmds_.end(), cmd) != want_cmds_.end();
want_cmds_.empty() || if (wanted)
std::find(want_cmds_.begin(), want_cmds_.end(), cmd) != want_cmds_.end(); {
if (wanted) {
command.template complete<I>(cmd, msg); command.template complete<I>(cmd, msg);
} }
}); });
@ -162,9 +185,9 @@ auto wait_timeout::start(Wait<Ts...>& command) -> void
{ {
timer_.emplace(command.get_connection().get_executor()); timer_.emplace(command.get_connection().get_executor());
timer_->expires_after(timeout_); timer_->expires_after(timeout_);
timer_->async_wait([this, &command](auto const& error) timer_->async_wait([this, &command](const auto &error) {
if (not error)
{ {
if (not error) {
timer_.reset(); timer_.reset();
command.template complete<I>(); command.template complete<I>();
} }
@ -176,7 +199,7 @@ auto Wait<Ts...>::await_suspend(std::coroutine_handle<irc_promise> handle) -> vo
{ {
handle_ = handle; handle_ = handle;
auto const tuple_size = std::tuple_size_v<decltype(modes_)>; const auto tuple_size = std::tuple_size_v<decltype(modes_)>;
start_modes(std::make_index_sequence<tuple_size>{}); start_modes(std::make_index_sequence<tuple_size>{});
disconnect_slot_ = get_connection().sig_disconnect.connect([this]() { disconnect_slot_ = get_connection().sig_disconnect.connect([this]() {
@ -187,14 +210,17 @@ auto Wait<Ts...>::await_suspend(std::coroutine_handle<irc_promise> handle) -> vo
template <typename... Ts> template <typename... Ts>
auto Wait<Ts...>::await_resume() -> std::variant<typename Ts::result_type...> auto Wait<Ts...>::await_resume() -> std::variant<typename Ts::result_type...>
{ {
auto const tuple_size = std::tuple_size_v<decltype(modes_)>; const auto tuple_size = std::tuple_size_v<decltype(modes_)>;
stop_modes(std::make_index_sequence<tuple_size>{}); stop_modes(std::make_index_sequence<tuple_size>{});
disconnect_slot_.disconnect(); disconnect_slot_.disconnect();
if (result_) { if (result_)
{
return std::move(*result_); return std::move(*result_);
} else { }
else
{
throw std::runtime_error{"connection terminated"}; throw std::runtime_error{"connection terminated"};
} }
} }

View File

@ -4,48 +4,57 @@
#include "ircmsg.hpp" #include "ircmsg.hpp"
namespace { namespace {
class Parser { class Parser
{
char *msg_; char *msg_;
inline static char empty[1]; inline static char empty[1];
inline void trim() { inline void trim()
while (*msg_ == ' ') msg_++; {
while (*msg_ == ' ')
msg_++;
} }
public: public:
Parser(char* msg) : msg_(msg) { Parser(char *msg)
if (msg_ == nullptr) { : msg_(msg)
{
if (msg_ == nullptr)
{
msg_ = empty; msg_ = empty;
} else { }
else
{
trim(); trim();
} }
} }
char* word() { char *word()
auto const start = msg_; {
while (*msg_ != '\0' && *msg_ != ' ') msg_++; const auto start = msg_;
if (*msg_ != '\0') { // prepare for next token while (*msg_ != '\0' && *msg_ != ' ')
msg_++;
if (*msg_ != '\0')
{ // prepare for next token
*msg_++ = '\0'; *msg_++ = '\0';
trim(); trim();
} }
return start; return start;
} }
bool match(char c) { bool match(char c)
if (c == *msg_) { {
if (c == *msg_)
{
msg_++; msg_++;
return true; return true;
} }
return false; return false;
} }
bool isempty() const { bool isempty() const { return *msg_ == '\0'; }
return *msg_ == '\0';
}
char const* peek() { const char *peek() { return msg_; }
return msg_;
}
}; };
std::string_view unescape_tag_value(char *const val) std::string_view unescape_tag_value(char *const val)
@ -53,7 +62,10 @@ std::string_view unescape_tag_value(char* const val)
// only start copying at the first escape character // only start copying at the first escape character
// skip everything before that // skip everything before that
auto cursor = strchr(val, '\\'); auto cursor = strchr(val, '\\');
if (cursor == nullptr) { return {val}; } if (cursor == nullptr)
{
return {val};
}
auto write = cursor; auto write = cursor;
for (; *cursor; cursor++) for (; *cursor; cursor++)
@ -63,12 +75,23 @@ std::string_view unescape_tag_value(char* const val)
cursor++; cursor++;
switch (*cursor) switch (*cursor)
{ {
default : *write++ = *cursor; break; default:
case ':' : *write++ = ';' ; break; *write++ = *cursor;
case 's' : *write++ = ' ' ; break; break;
case 'r' : *write++ = '\r' ; break; case ':':
case 'n' : *write++ = '\n' ; break; *write++ = ';';
case '\0': return {val, write}; break;
case 's':
*write++ = ' ';
break;
case 'r':
*write++ = '\r';
break;
case 'n':
*write++ = '\n';
break;
case '\0':
return {val, write};
} }
} }
else else
@ -85,18 +108,24 @@ auto parse_irc_tags(char* str) -> std::vector<irctag>
{ {
std::vector<irctag> tags; std::vector<irctag> tags;
do { do
{
auto val = strsep(&str, ";"); auto val = strsep(&str, ";");
auto key = strsep(&val, "="); auto key = strsep(&val, "=");
if ('\0' == *key) { if ('\0' == *key)
{
throw irc_parse_error(irc_error_code::MISSING_TAG); throw irc_parse_error(irc_error_code::MISSING_TAG);
} }
if (nullptr == val) { if (nullptr == val)
{
tags.emplace_back(key, ""); tags.emplace_back(key, "");
} else { }
else
{
tags.emplace_back(std::string_view{key, val - 1}, unescape_tag_value(val)); tags.emplace_back(std::string_view{key, val - 1}, unescape_tag_value(val));
} }
} while(nullptr != str); }
while (nullptr != str);
return tags; return tags;
} }
@ -107,24 +136,29 @@ auto parse_irc_message(char* msg) -> IrcMsg
IrcMsg out; IrcMsg out;
/* MESSAGE TAGS */ /* MESSAGE TAGS */
if (p.match('@')) { if (p.match('@'))
{
out.tags = parse_irc_tags(p.word()); out.tags = parse_irc_tags(p.word());
} }
/* MESSAGE SOURCE */ /* MESSAGE SOURCE */
if (p.match(':')) { if (p.match(':'))
{
out.source = p.word(); out.source = p.word();
} }
/* MESSAGE COMMANDS */ /* MESSAGE COMMANDS */
out.command = p.word(); out.command = p.word();
if (out.command.empty()) { if (out.command.empty())
{
throw irc_parse_error{irc_error_code::MISSING_COMMAND}; throw irc_parse_error{irc_error_code::MISSING_COMMAND};
} }
/* MESSAGE ARGUMENTS */ /* MESSAGE ARGUMENTS */
while (!p.isempty()) { while (!p.isempty())
if (p.match(':')) { {
if (p.match(':'))
{
out.args.push_back(p.peek()); out.args.push_back(p.peek());
break; break;
} }
@ -134,16 +168,19 @@ auto parse_irc_message(char* msg) -> IrcMsg
return out; return out;
} }
auto IrcMsg::hassource() const -> bool auto IrcMsg::hassource() const -> bool { return source.data() != nullptr; }
{
return source.data() != nullptr;
}
auto operator<<(std::ostream &out, irc_error_code code) -> std::ostream & auto operator<<(std::ostream &out, irc_error_code code) -> std::ostream &
{ {
switch(code) { switch (code)
case irc_error_code::MISSING_COMMAND: out << "MISSING COMMAND"; return out; {
case irc_error_code::MISSING_TAG: out << "MISSING TAG"; return out; case irc_error_code::MISSING_COMMAND:
default: return out; out << "MISSING COMMAND";
return out;
case irc_error_code::MISSING_TAG:
out << "MISSING TAG";
return out;
default:
return out;
} }
} }

View File

@ -9,9 +9,13 @@ struct irctag
std::string_view key; std::string_view key;
std::string_view val; std::string_view val;
irctag(std::string_view key, std::string_view val) : key{key}, val{val} {} irctag(std::string_view key, std::string_view val)
: key{key}
, val{val}
{
}
friend auto operator==(irctag const&, irctag const&) -> bool = default; friend auto operator==(const irctag &, const irctag &) -> bool = default;
}; };
struct IrcMsg struct IrcMsg
@ -27,27 +31,35 @@ struct IrcMsg
std::vector<irctag> &&tags, std::vector<irctag> &&tags,
std::string_view source, std::string_view source,
std::string_view command, std::string_view command,
std::vector<std::string_view> && args) std::vector<std::string_view> &&args
: tags(std::move(tags)), )
args(std::move(args)), : tags(std::move(tags))
source{source}, , args(std::move(args))
command{command} {} , source{source}
, command{command}
{
}
bool hassource() const; bool hassource() const;
friend bool operator==(IrcMsg const&, IrcMsg const&) = default; friend bool operator==(const IrcMsg &, const IrcMsg &) = default;
}; };
enum class irc_error_code { enum class irc_error_code
{
MISSING_TAG, MISSING_TAG,
MISSING_COMMAND, MISSING_COMMAND,
}; };
auto operator<<(std::ostream &out, irc_error_code) -> std::ostream &; auto operator<<(std::ostream &out, irc_error_code) -> std::ostream &;
struct irc_parse_error : public std::exception { struct irc_parse_error : public std::exception
{
irc_error_code code; irc_error_code code;
irc_parse_error(irc_error_code code) : code(code) {} irc_parse_error(irc_error_code code)
: code(code)
{
}
}; };
/** /**

View File

@ -34,7 +34,11 @@ public:
* *
* @param n Buffer size * @param n Buffer size
*/ */
LineBuffer(std::size_t n) : buffer(n), end_{buffer.begin()} {} LineBuffer(std::size_t n)
: buffer(n)
, end_{buffer.begin()}
{
}
/** /**
* @brief Get the available buffer space * @brief Get the available buffer space
@ -60,7 +64,7 @@ public:
*/ */
auto add_bytes(std::size_t n, std::invocable<char *> auto line_cb) -> void auto add_bytes(std::size_t n, std::invocable<char *> auto line_cb) -> void
{ {
auto const start = end_; const auto start = end_;
std::advance(end_, n); std::advance(end_, n);
// new data is now located in [start, end_) // new data is now located in [start, end_)

View File

@ -13,11 +13,11 @@
using namespace std::chrono_literals; using namespace std::chrono_literals;
auto start(boost::asio::io_context & io, Settings const& settings) -> void auto start(boost::asio::io_context &io, const Settings &settings) -> void
{ {
auto const connection = std::make_shared<Connection>(io); const auto connection = std::make_shared<Connection>(io);
auto const selfThread = SelfThread::start(*connection); const auto selfThread = SelfThread::start(*connection);
RegistrationThread::start(*connection, settings, selfThread); RegistrationThread::start(*connection, settings, selfThread);
connection->sig_snote.connect([](auto &match) { connection->sig_snote.connect([](auto &match) {
@ -29,17 +29,16 @@ auto start(boost::asio::io_context & io, Settings const& settings) -> void
}); });
boost::asio::co_spawn( boost::asio::co_spawn(
io, io, connection->connect(io, settings.host, settings.service),
connection->connect(io, settings.host, settings.service), [&io, &settings](std::exception_ptr e) {
[&io, &settings](std::exception_ptr e)
{
auto timer = std::make_shared<boost::asio::steady_timer>(io); auto timer = std::make_shared<boost::asio::steady_timer>(io);
timer->expires_after(5s); timer->expires_after(5s);
timer->async_wait([&io, &settings, timer](auto) { timer->async_wait(
start(io, settings); [&io, &settings, timer](auto) { start(io, settings); }
}); );
}); }
);
} }
auto get_settings() -> Settings auto get_settings() -> Settings
@ -57,7 +56,7 @@ auto get_settings() -> Settings
auto main() -> int auto main() -> int
{ {
auto const settings = get_settings(); const auto settings = get_settings();
auto io = boost::asio::io_context{}; auto io = boost::asio::io_context{};
start(io, settings); start(io, settings);
io.run(); io.run();

View File

@ -22,7 +22,10 @@ RegistrationThread::RegistrationThread(
auto RegistrationThread::on_connect() -> void auto RegistrationThread::on_connect() -> void
{ {
connection_.send_cap_ls(); connection_.send_cap_ls();
if (not settings_.password.empty())
{
connection_.send_pass(settings_.password); connection_.send_pass(settings_.password);
}
connection_.send_user(settings_.username, settings_.realname); connection_.send_user(settings_.username, settings_.realname);
connection_.send_nick(settings_.nickname); connection_.send_nick(settings_.nickname);
} }
@ -30,7 +33,7 @@ auto RegistrationThread::on_connect() -> void
auto RegistrationThread::send_req() -> void auto RegistrationThread::send_req() -> void
{ {
std::string request; std::string request;
std::vector<char const*> want { std::vector<const char *> want{
"account-notify", "account-notify",
"account-tag", "account-tag",
"batch", "batch",
@ -46,11 +49,12 @@ auto RegistrationThread::send_req() -> void
"solanum.chat/realhost", "solanum.chat/realhost",
}; };
if (settings_.sasl_mechanism == "PLAIN") { if (not settings_.sasl_mechanism.empty())
{
want.push_back("sasl"); want.push_back("sasl");
} }
for (auto const cap : want) for (const auto cap : want)
{ {
if (caps.contains(cap)) if (caps.contains(cap))
{ {
@ -73,7 +77,7 @@ auto RegistrationThread::send_req() -> void
} }
} }
auto RegistrationThread::on_msg_cap_ack(IrcMsg const& msg) -> void auto RegistrationThread::on_msg_cap_ack(const IrcMsg &msg) -> void
{ {
auto in = std::istringstream{std::string{msg.args[2]}}; auto in = std::istringstream{std::string{msg.args[2]}};
std::for_each( std::for_each(
@ -87,13 +91,18 @@ auto RegistrationThread::on_msg_cap_ack(IrcMsg const& msg) -> void
{ {
message_handle_.disconnect(); message_handle_.disconnect();
if (settings_.sasl_mechanism.empty()) { if (settings_.sasl_mechanism.empty())
{
connection_.send_cap_end(); connection_.send_cap_end();
} else { }
else
{
self_->start_sasl(std::make_unique<SaslPlain>(settings_.sasl_authcid, settings_.sasl_authzid, settings_.sasl_password)); self_->start_sasl(std::make_unique<SaslPlain>(settings_.sasl_authcid, settings_.sasl_authzid, settings_.sasl_password));
connection_.sig_ircmsg.connect_extended([thread = shared_from_this()](auto &slot, auto cmd, auto &msg) { connection_.sig_ircmsg.connect_extended([thread = shared_from_this()](auto &slot, auto cmd, auto &msg) {
switch (cmd) { switch (cmd)
default: break; {
default:
break;
case IrcCommand::RPL_SASLSUCCESS: case IrcCommand::RPL_SASLSUCCESS:
case IrcCommand::ERR_SASLFAIL: case IrcCommand::ERR_SASLFAIL:
thread->connection_.send_cap_end(); thread->connection_.send_cap_end();
@ -104,9 +113,9 @@ auto RegistrationThread::on_msg_cap_ack(IrcMsg const& msg) -> void
} }
} }
auto RegistrationThread::on_msg_cap_ls(IrcMsg const& msg) -> void auto RegistrationThread::on_msg_cap_ls(const IrcMsg &msg) -> void
{ {
std::string_view const* kvs; const std::string_view *kvs;
bool last; bool last;
if (3 == msg.args.size()) if (3 == msg.args.size())
@ -130,7 +139,7 @@ auto RegistrationThread::on_msg_cap_ls(IrcMsg const& msg) -> void
std::istream_iterator<std::string>{in}, std::istream_iterator<std::string>{in},
std::istream_iterator<std::string>{}, std::istream_iterator<std::string>{},
[this](std::string x) { [this](std::string x) {
auto const eq = x.find('='); const auto eq = x.find('=');
if (eq == x.npos) if (eq == x.npos)
{ {
caps.emplace(x, std::string{}); caps.emplace(x, std::string{});
@ -155,12 +164,11 @@ auto RegistrationThread::start(
std::shared_ptr<SelfThread> self std::shared_ptr<SelfThread> self
) -> std::shared_ptr<RegistrationThread> ) -> std::shared_ptr<RegistrationThread>
{ {
auto const thread = std::make_shared<RegistrationThread>(connection, std::move(settings), std::move(self)); const auto thread = std::make_shared<RegistrationThread>(connection, std::move(settings), std::move(self));
thread->listen_for_cap_ls(); thread->listen_for_cap_ls();
thread->connect_handle_ = connection.sig_connect.connect([thread]() thread->connect_handle_ = connection.sig_connect.connect([thread]() {
{
thread->connect_handle_.disconnect(); thread->connect_handle_.disconnect();
thread->on_connect(); thread->on_connect();
}); });
@ -170,8 +178,7 @@ auto RegistrationThread::start(
auto RegistrationThread::listen_for_cap_ack() -> void auto RegistrationThread::listen_for_cap_ack() -> void
{ {
message_handle_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, IrcMsg const& msg) message_handle_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, const IrcMsg &msg) {
{
if (IrcCommand::CAP == cmd && msg.args.size() >= 2 && "ACK" == msg.args[1]) if (IrcCommand::CAP == cmd && msg.args.size() >= 2 && "ACK" == msg.args[1])
{ {
thread->on_msg_cap_ack(msg); thread->on_msg_cap_ack(msg);
@ -181,11 +188,15 @@ auto RegistrationThread::listen_for_cap_ack() -> void
auto RegistrationThread::listen_for_cap_ls() -> void auto RegistrationThread::listen_for_cap_ls() -> void
{ {
message_handle_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, IrcMsg const& msg) message_handle_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, const IrcMsg &msg) {
{
if (IrcCommand::CAP == cmd && msg.args.size() >= 2 && "LS" == msg.args[1]) if (IrcCommand::CAP == cmd && msg.args.size() >= 2 && "LS" == msg.args[1])
{ {
thread->on_msg_cap_ls(msg); thread->on_msg_cap_ls(msg);
} }
else if (IrcCommand::RPL_WELCOME == cmd)
{
// Server doesn't support CAP negotiation
thread->message_handle_.disconnect();
}
}); });
} }

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "connection.hpp" #include "connection.hpp"
#include "settings.hpp"
#include "self_thread.hpp" #include "self_thread.hpp"
#include "settings.hpp"
#include <memory> #include <memory>
#include <string> #include <string>
@ -23,8 +23,8 @@ class RegistrationThread : public std::enable_shared_from_this<RegistrationThrea
auto on_connect() -> void; auto on_connect() -> void;
auto send_req() -> void; auto send_req() -> void;
auto on_msg_cap_ls(IrcMsg const& msg) -> void; auto on_msg_cap_ls(const IrcMsg &msg) -> void;
auto on_msg_cap_ack(IrcMsg const& msg) -> void; auto on_msg_cap_ack(const IrcMsg &msg) -> void;
auto listen_for_cap_ack() -> void; auto listen_for_cap_ack() -> void;
auto listen_for_cap_ls() -> void; auto listen_for_cap_ls() -> void;

View File

@ -7,10 +7,6 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include "event.hpp"
struct Connection;
class SaslMechanism class SaslMechanism
{ {
public: public:

View File

@ -9,12 +9,12 @@
using namespace std::literals; using namespace std::literals;
auto SelfThread::on_welcome(IrcMsg const& irc) -> void auto SelfThread::on_welcome(const IrcMsg &irc) -> void
{ {
nickname_ = irc.args[0]; nickname_ = irc.args[0];
} }
auto SelfThread::on_nick(IrcMsg const& irc) -> void auto SelfThread::on_nick(const IrcMsg &irc) -> void
{ {
if (is_my_mask(irc.source)) if (is_my_mask(irc.source))
{ {
@ -22,12 +22,12 @@ auto SelfThread::on_nick(IrcMsg const& irc) -> void
} }
} }
auto SelfThread::on_umodeis(IrcMsg const& irc) -> void auto SelfThread::on_umodeis(const IrcMsg &irc) -> void
{ {
mode_ = irc.args[1]; mode_ = irc.args[1];
} }
auto SelfThread::on_join(IrcMsg const& irc) -> void auto SelfThread::on_join(const IrcMsg &irc) -> void
{ {
if (is_my_mask(irc.source)) if (is_my_mask(irc.source))
{ {
@ -35,7 +35,7 @@ auto SelfThread::on_join(IrcMsg const& irc) -> void
} }
} }
auto SelfThread::on_kick(IrcMsg const& irc) -> void auto SelfThread::on_kick(const IrcMsg &irc) -> void
{ {
if (is_my_nick(irc.args[1])) if (is_my_nick(irc.args[1]))
{ {
@ -43,7 +43,7 @@ auto SelfThread::on_kick(IrcMsg const& irc) -> void
} }
} }
auto SelfThread::on_part(IrcMsg const& irc) -> void auto SelfThread::on_part(const IrcMsg &irc) -> void
{ {
if (is_my_mask(irc.source)) if (is_my_mask(irc.source))
{ {
@ -51,12 +51,12 @@ auto SelfThread::on_part(IrcMsg const& irc) -> void
} }
} }
auto SelfThread::on_mode(IrcMsg const& irc) -> void auto SelfThread::on_mode(const IrcMsg &irc) -> void
{ {
if (is_my_nick(irc.args[0])) if (is_my_nick(irc.args[0]))
{ {
auto polarity = true; auto polarity = true;
for (char const c : irc.args[1]) for (const char c : irc.args[1])
{ {
switch (c) switch (c)
{ {
@ -73,7 +73,7 @@ auto SelfThread::on_mode(IrcMsg const& irc) -> void
} }
else else
{ {
auto const ix = mode_.find(c); const auto ix = mode_.find(c);
if (ix != std::string::npos) if (ix != std::string::npos)
{ {
mode_.erase(ix, 1); mode_.erase(ix, 1);
@ -87,20 +87,26 @@ auto SelfThread::on_mode(IrcMsg const& irc) -> void
auto SelfThread::on_isupport(const IrcMsg &msg) -> void auto SelfThread::on_isupport(const IrcMsg &msg) -> void
{ {
auto const hi = msg.args.size() - 1; const auto hi = msg.args.size() - 1;
for (int i = 1; i < hi; ++i) for (int i = 1; i < hi; ++i)
{ {
auto &entry = msg.args[i]; auto &entry = msg.args[i];
// Leading minus means to stop support // Leading minus means to stop support
if (entry.starts_with("-")) { if (entry.starts_with("-"))
auto const key = std::string{entry.substr(1)}; {
if (auto cursor = isupport_.find(key); cursor != isupport_.end()) { const auto key = std::string{entry.substr(1)};
if (auto cursor = isupport_.find(key); cursor != isupport_.end())
{
isupport_.erase(cursor); isupport_.erase(cursor);
} }
} else if (auto const cursor = entry.find('='); cursor != entry.npos) { }
else if (const auto cursor = entry.find('='); cursor != entry.npos)
{
isupport_.emplace(entry.substr(0, cursor), entry.substr(cursor + 1)); isupport_.emplace(entry.substr(0, cursor), entry.substr(cursor + 1));
} else { }
else
{
isupport_.emplace(entry, std::string{}); isupport_.emplace(entry, std::string{});
} }
} }
@ -110,19 +116,35 @@ auto SelfThread::start(Connection& connection) -> std::shared_ptr<SelfThread>
{ {
auto thread = std::make_shared<SelfThread>(connection); auto thread = std::make_shared<SelfThread>(connection);
connection.sig_ircmsg.connect([thread](auto cmd, auto& msg) connection.sig_ircmsg.connect([thread](auto cmd, auto &msg) {
{
switch (cmd) switch (cmd)
{ {
case IrcCommand::JOIN: thread->on_join(msg); break; case IrcCommand::JOIN:
case IrcCommand::KICK: thread->on_kick(msg); break; thread->on_join(msg);
case IrcCommand::MODE: thread->on_mode(msg); break; break;
case IrcCommand::NICK: thread->on_nick(msg); break; case IrcCommand::KICK:
case IrcCommand::PART: thread->on_part(msg); break; thread->on_kick(msg);
case IrcCommand::RPL_ISUPPORT: thread->on_isupport(msg); break; break;
case IrcCommand::RPL_UMODEIS: thread->on_umodeis(msg); break; case IrcCommand::MODE:
case IrcCommand::RPL_WELCOME: thread->on_welcome(msg); break; thread->on_mode(msg);
default: break; break;
case IrcCommand::NICK:
thread->on_nick(msg);
break;
case IrcCommand::PART:
thread->on_part(msg);
break;
case IrcCommand::RPL_ISUPPORT:
thread->on_isupport(msg);
break;
case IrcCommand::RPL_UMODEIS:
thread->on_umodeis(msg);
break;
case IrcCommand::RPL_WELCOME:
thread->on_welcome(msg);
break;
default:
break;
} }
}); });
@ -133,17 +155,17 @@ auto SelfThread::start(Connection& connection) -> std::shared_ptr<SelfThread>
return thread; return thread;
} }
auto SelfThread::get_my_nickname() const -> std::string const& auto SelfThread::get_my_nickname() const -> const std::string &
{ {
return nickname_; return nickname_;
} }
auto SelfThread::get_my_mode() const -> std::string const& auto SelfThread::get_my_mode() const -> const std::string &
{ {
return mode_; return mode_;
} }
auto SelfThread::get_my_channels() const -> std::unordered_set<std::string> const& auto SelfThread::get_my_channels() const -> const std::unordered_set<std::string> &
{ {
return channels_; return channels_;
} }
@ -155,7 +177,7 @@ auto SelfThread::is_my_nick(std::string_view nick) const -> bool
auto SelfThread::is_my_mask(std::string_view mask) const -> bool auto SelfThread::is_my_mask(std::string_view mask) const -> bool
{ {
auto const bang = mask.find('!'); const auto bang = mask.find('!');
return bang != std::string_view::npos && nickname_ == mask.substr(0, bang); return bang != std::string_view::npos && nickname_ == mask.substr(0, bang);
} }
@ -168,7 +190,8 @@ auto SelfThread::on_authenticate(const std::string_view body) -> void
return; return;
} }
if (auto reply = sasl_mechanism_->step(body)) { if (auto reply = sasl_mechanism_->step(body))
{
connection_.send_authenticate_encoded(*reply); connection_.send_authenticate_encoded(*reply);
@ -182,7 +205,8 @@ auto SelfThread::on_authenticate(const std::string_view body) -> void
auto SelfThread::start_sasl(std::unique_ptr<SaslMechanism> mechanism) -> void auto SelfThread::start_sasl(std::unique_ptr<SaslMechanism> mechanism) -> void
{ {
if (sasl_mechanism_) { if (sasl_mechanism_)
{
connection_.send_authenticate("*"sv); // abort SASL connection_.send_authenticate("*"sv); // abort SASL
} }

View File

@ -26,27 +26,29 @@ class SelfThread
std::unique_ptr<SaslMechanism> sasl_mechanism_; std::unique_ptr<SaslMechanism> sasl_mechanism_;
auto on_welcome(IrcMsg const& irc) -> void; auto on_welcome(const IrcMsg &irc) -> void;
auto on_isupport(IrcMsg const& irc) -> void; auto on_isupport(const IrcMsg &irc) -> void;
auto on_nick(IrcMsg const& irc) -> void; auto on_nick(const IrcMsg &irc) -> void;
auto on_umodeis(IrcMsg const& irc) -> void; auto on_umodeis(const IrcMsg &irc) -> void;
auto on_join(IrcMsg const& irc) -> void; auto on_join(const IrcMsg &irc) -> void;
auto on_kick(IrcMsg const& irc) -> void; auto on_kick(const IrcMsg &irc) -> void;
auto on_part(IrcMsg const& irc) -> void; auto on_part(const IrcMsg &irc) -> void;
auto on_mode(IrcMsg const& irc) -> void; auto on_mode(const IrcMsg &irc) -> void;
auto on_authenticate(std::string_view) -> void; auto on_authenticate(std::string_view) -> void;
public: public:
SelfThread(Connection& connection) : connection_{connection} {} SelfThread(Connection &connection)
: connection_{connection}
{
}
static auto start(Connection &) -> std::shared_ptr<SelfThread>; static auto start(Connection &) -> std::shared_ptr<SelfThread>;
auto start_sasl(std::unique_ptr<SaslMechanism> mechanism) -> void; auto start_sasl(std::unique_ptr<SaslMechanism> mechanism) -> void;
auto get_my_nickname() const -> std::string const&; auto get_my_nickname() const -> const std::string &;
auto get_my_mode() const -> std::string const&; auto get_my_mode() const -> const std::string &;
auto get_my_channels() const -> std::unordered_set<std::string> const&; auto get_my_channels() const -> const std::unordered_set<std::string> &;
auto is_my_nick(std::string_view nick) const -> bool; auto is_my_nick(std::string_view nick) const -> bool;
auto is_my_mask(std::string_view nick) const -> bool; auto is_my_mask(std::string_view nick) const -> bool;
}; };

View File

@ -5,7 +5,7 @@
auto Settings::from_stream(std::istream &in) -> Settings auto Settings::from_stream(std::istream &in) -> Settings
{ {
auto const config = toml::parse(in); const auto config = toml::parse(in);
return Settings{ return Settings{
.host = config["host"].value_or(std::string{}), .host = config["host"].value_or(std::string{}),
.service = config["service"].value_or(std::string{}), .service = config["service"].value_or(std::string{}),

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <string>
#include <istream> #include <istream>
#include <string>
struct Settings struct Settings
{ {
@ -19,4 +19,3 @@ struct Settings
static auto from_stream(std::istream &in) -> Settings; static auto from_stream(std::istream &in) -> Settings;
}; };

View File

@ -17,7 +17,7 @@ namespace {
struct SnotePattern struct SnotePattern
{ {
SnotePattern(SnoteTag tag, char const* expression, unsigned flags = 0) SnotePattern(SnoteTag tag, const char *expression, unsigned flags = 0)
: tag{tag} : tag{tag}
, expression{expression} , expression{expression}
, regex{expression, std::regex_constants::ECMAScript | std::regex_constants::optimize} , regex{expression, std::regex_constants::ECMAScript | std::regex_constants::optimize}
@ -25,12 +25,11 @@ struct SnotePattern
} }
SnoteTag tag; SnoteTag tag;
char const* expression; const char *expression;
std::regex regex; std::regex regex;
}; };
SnotePattern static const patterns[] = const SnotePattern static patterns[] = {
{
{SnoteTag::ClientConnecting, {SnoteTag::ClientConnecting,
R"(^Client connecting: ([^ ]+) \(([^@ ]+)@([^) ]+)\) \[(.*)\] \{([^ ]*)\} <([^ ]*)> \[(.*)\]$)"}, R"(^Client connecting: ([^ ]+) \(([^@ ]+)@([^) ]+)\) \[(.*)\] \{([^ ]*)\} <([^ ]*)> \[(.*)\]$)"},
@ -79,8 +78,8 @@ SnotePattern static const patterns[] =
static auto setup_database() -> hs_database_t * static auto setup_database() -> hs_database_t *
{ {
auto const n = std::size(patterns); const auto n = std::size(patterns);
std::vector<char const*> expressions; std::vector<const char *> expressions;
std::vector<unsigned> flags(n, HS_FLAG_SINGLEMATCH); std::vector<unsigned> flags(n, HS_FLAG_SINGLEMATCH);
std::vector<unsigned> ids; std::vector<unsigned> ids;
@ -98,8 +97,7 @@ static auto setup_database() -> hs_database_t*
hs_platform_info_t *platform = nullptr; // target current platform hs_platform_info_t *platform = nullptr; // target current platform
switch (hs_compile_multi(expressions.data(), flags.data(), ids.data(), expressions.size(), HS_MODE_BLOCK, platform, &db, &error)) switch (hs_compile_multi(expressions.data(), flags.data(), ids.data(), expressions.size(), HS_MODE_BLOCK, platform, &db, &error))
{ {
case HS_COMPILER_ERROR: case HS_COMPILER_ERROR: {
{
std::string msg = error->message; std::string msg = error->message;
hs_free_compile_error(error); hs_free_compile_error(error);
throw std::runtime_error{std::move(msg)}; throw std::runtime_error{std::move(msg)};
@ -128,24 +126,23 @@ SnoteCore::SnoteCore()
auto SnoteCore::match(const IrcMsg &msg) -> std::optional<SnoteMatch> auto SnoteCore::match(const IrcMsg &msg) -> std::optional<SnoteMatch>
{ {
static char const* const prefix = "*** Notice -- "; static const char *const prefix = "*** Notice -- ";
auto &args = msg.args; auto &args = msg.args;
if ("*" != args[0] || !args[1].starts_with(prefix)) { if ("*" != args[0] || !args[1].starts_with(prefix))
{
return std::nullopt; return std::nullopt;
} }
auto const message = args[1].substr(strlen(prefix)); const auto message = args[1].substr(strlen(prefix));
unsigned match_id; unsigned match_id;
auto cb = [&match_id](unsigned id, unsigned long long, unsigned long long, unsigned) -> int auto cb = [&match_id](unsigned id, unsigned long long, unsigned long long, unsigned) -> int {
{
match_id = id; match_id = id;
return 1; // stop scanning return 1; // stop scanning
}; };
auto const scan_result = const auto scan_result = hs_scan(
hs_scan(
db_.get(), db_.get(),
message.data(), message.size(), message.data(), message.size(),
0, // no flags 0, // no flags
@ -159,8 +156,7 @@ auto SnoteCore::match(const IrcMsg &msg) -> std::optional<SnoteMatch>
BOOST_LOG_TRIVIAL(warning) << "Unknown snote: " << message; BOOST_LOG_TRIVIAL(warning) << "Unknown snote: " << message;
return std::nullopt; return std::nullopt;
case HS_SCAN_TERMINATED: case HS_SCAN_TERMINATED: {
{
auto &pattern = patterns[match_id]; auto &pattern = patterns[match_id];
return SnoteMatch{pattern.tag, pattern.regex, message}; return SnoteMatch{pattern.tag, pattern.regex, message};
} }
@ -170,9 +166,10 @@ auto SnoteCore::match(const IrcMsg &msg) -> std::optional<SnoteMatch>
} }
} }
auto SnoteMatch::get_results() -> std::match_results<std::string_view::const_iterator> const& auto SnoteMatch::get_results() -> const std::match_results<std::string_view::const_iterator> &
{
if (auto results = std::get_if<1>(&components_))
{ {
if (auto results = std::get_if<1>(&components_)) {
return *results; return *results;
} }