mass reformat
This commit is contained in:
parent
fd4612d385
commit
093515c3ec
232
.clang-format
Normal file
232
.clang-format
Normal 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: []
|
@ -12,11 +12,11 @@ namespace {
|
|||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
Connection::Connection(boost::asio::io_context & io)
|
Connection::Connection(boost::asio::io_context &io)
|
||||||
: stream_{io}
|
: stream_{io}
|
||||||
, watchdog_timer_{io}
|
, watchdog_timer_{io}
|
||||||
, write_posted_{false}
|
, write_posted_{false}
|
||||||
, stalled_{false}
|
, stalled_{false}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,28 +24,32 @@ 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 (not error) {
|
{
|
||||||
if (write_strings_.empty()) {
|
if (write_strings_.empty())
|
||||||
|
{
|
||||||
write_posted_ = false;
|
write_posted_ = false;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
write_buffers();
|
write_buffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
write_strings_.clear();
|
write_strings_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Connection::connect(
|
auto Connection::connect(
|
||||||
boost::asio::io_context & io,
|
boost::asio::io_context &io,
|
||||||
std::string host,
|
std::string host,
|
||||||
std::string port
|
std::string port
|
||||||
) -> boost::asio::awaitable<void>
|
) -> boost::asio::awaitable<void>
|
||||||
@ -53,29 +57,28 @@ 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;
|
||||||
}
|
}
|
||||||
buffer.add_bytes(n, [this](char * line) {
|
buffer.add_bytes(n, [this](char *line) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "RECV: " << line;
|
BOOST_LOG_TRIVIAL(debug) << "RECV: " << line;
|
||||||
watchdog_activity();
|
watchdog_activity();
|
||||||
dispatch_line(line);
|
dispatch_line(line);
|
||||||
@ -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,9 +171,10 @@ 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())
|
||||||
{
|
{
|
||||||
self->write_buffers();
|
self->write_buffers();
|
||||||
@ -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,9 +309,10 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,4 +320,4 @@ auto Connection::send_authenticate_encoded(std::string_view body) -> void
|
|||||||
{
|
{
|
||||||
send_authenticate("+"sv);
|
send_authenticate("+"sv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
@ -27,7 +27,7 @@ private:
|
|||||||
std::string authenticate_buffer_;
|
std::string authenticate_buffer_;
|
||||||
|
|
||||||
auto write_buffers() -> void;
|
auto write_buffers() -> void;
|
||||||
auto dispatch_line(char * line) -> void;
|
auto dispatch_line(char *line) -> void;
|
||||||
|
|
||||||
static constexpr std::chrono::seconds watchdog_duration = std::chrono::seconds{30};
|
static constexpr std::chrono::seconds watchdog_duration = std::chrono::seconds{30};
|
||||||
auto watchdog() -> void;
|
auto watchdog() -> void;
|
||||||
@ -40,10 +40,10 @@ private:
|
|||||||
auto write_irc(std::string) -> void;
|
auto write_irc(std::string) -> void;
|
||||||
auto write_irc(std::string, std::string_view) -> void;
|
auto write_irc(std::string, std::string_view) -> void;
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
auto write_irc(std::string front, std::string_view next, Args ...rest) -> void;
|
auto write_irc(std::string front, std::string_view next, Args... rest) -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Connection(boost::asio::io_context & io);
|
Connection(boost::asio::io_context &io);
|
||||||
|
|
||||||
boost::signals2::signal<void()> sig_connect;
|
boost::signals2::signal<void()> sig_connect;
|
||||||
boost::signals2::signal<void()> sig_disconnect;
|
boost::signals2::signal<void()> sig_disconnect;
|
||||||
@ -51,12 +51,13 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto connect(
|
auto connect(
|
||||||
boost::asio::io_context & io,
|
boost::asio::io_context &io,
|
||||||
std::string host,
|
std::string host,
|
||||||
std::string port
|
std::string port
|
||||||
) -> boost::asio::awaitable<void>;
|
) -> boost::asio::awaitable<void>;
|
||||||
@ -81,16 +82,15 @@ 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 == ' ';
|
||||||
};
|
};
|
||||||
|
|
||||||
if (next.empty()
|
if (next.empty()
|
||||||
|| next.front() == ':'
|
|| next.front() == ':'
|
||||||
|| next.end() != std::find_if(next.begin(), next.end(), is_invalid))
|
|| next.end() != std::find_if(next.begin(), next.end(), is_invalid))
|
||||||
{
|
{
|
||||||
throw std::runtime_error{"bad irc argument"};
|
throw std::runtime_error{"bad irc argument"};
|
||||||
}
|
}
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
|
@ -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,22 +45,25 @@ 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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename ... Ts>
|
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,23 +172,22 @@ 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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t I, typename... Ts>
|
template <size_t I, typename... Ts>
|
||||||
auto wait_timeout::start(Wait<Ts...>& command) -> void
|
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"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
137
ircmsg.cpp
137
ircmsg.cpp
@ -4,56 +4,68 @@
|
|||||||
#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)
|
||||||
{
|
{
|
||||||
// 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
|
||||||
@ -81,50 +104,61 @@ std::string_view unescape_tag_value(char* const val)
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
auto parse_irc_tags(char* str) -> std::vector<irctag>
|
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 {
|
|
||||||
tags.emplace_back(std::string_view{key, val-1}, unescape_tag_value(val));
|
|
||||||
}
|
}
|
||||||
} while(nullptr != str);
|
else
|
||||||
|
{
|
||||||
|
tags.emplace_back(std::string_view{key, val - 1}, unescape_tag_value(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (nullptr != str);
|
||||||
|
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto parse_irc_message(char* msg) -> IrcMsg
|
auto parse_irc_message(char *msg) -> IrcMsg
|
||||||
{
|
{
|
||||||
Parser p {msg};
|
Parser p{msg};
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
ircmsg.hpp
44
ircmsg.hpp
@ -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
|
||||||
@ -24,30 +28,38 @@ struct IrcMsg
|
|||||||
IrcMsg() = default;
|
IrcMsg() = default;
|
||||||
|
|
||||||
IrcMsg(
|
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)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,6 +69,6 @@ struct irc_parse_error : public std::exception {
|
|||||||
*
|
*
|
||||||
* Returns zero for success, non-zero for parse error.
|
* Returns zero for success, non-zero for parse error.
|
||||||
*/
|
*/
|
||||||
auto parse_irc_message(char* msg) -> IrcMsg;
|
auto parse_irc_message(char *msg) -> IrcMsg;
|
||||||
|
|
||||||
auto parse_irc_tags(char* msg) -> std::vector<irctag>;
|
auto parse_irc_tags(char *msg) -> std::vector<irctag>;
|
||||||
|
@ -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_)
|
||||||
|
25
main.cpp
25
main.cpp
@ -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,22 +29,21 @@ 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
|
||||||
{
|
{
|
||||||
if (auto config_stream = std::ifstream {"config.toml"})
|
if (auto config_stream = std::ifstream{"config.toml"})
|
||||||
{
|
{
|
||||||
return Settings::from_stream(config_stream);
|
return Settings::from_stream(config_stream);
|
||||||
}
|
}
|
||||||
@ -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();
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
RegistrationThread::RegistrationThread(
|
RegistrationThread::RegistrationThread(
|
||||||
Connection& connection,
|
Connection &connection,
|
||||||
const Settings &settings,
|
const Settings &settings,
|
||||||
std::shared_ptr<SelfThread> self
|
std::shared_ptr<SelfThread> self
|
||||||
)
|
)
|
||||||
@ -22,7 +22,10 @@ RegistrationThread::RegistrationThread(
|
|||||||
auto RegistrationThread::on_connect() -> void
|
auto RegistrationThread::on_connect() -> void
|
||||||
{
|
{
|
||||||
connection_.send_cap_ls();
|
connection_.send_cap_ls();
|
||||||
connection_.send_pass(settings_.password);
|
if (not settings_.password.empty())
|
||||||
|
{
|
||||||
|
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(
|
||||||
@ -86,27 +90,32 @@ auto RegistrationThread::on_msg_cap_ack(IrcMsg const& msg) -> void
|
|||||||
if (outstanding.empty())
|
if (outstanding.empty())
|
||||||
{
|
{
|
||||||
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;
|
{
|
||||||
case IrcCommand::RPL_SASLSUCCESS:
|
default:
|
||||||
case IrcCommand::ERR_SASLFAIL:
|
break;
|
||||||
thread->connection_.send_cap_end();
|
case IrcCommand::RPL_SASLSUCCESS:
|
||||||
slot.disconnect();
|
case IrcCommand::ERR_SASLFAIL:
|
||||||
|
thread->connection_.send_cap_end();
|
||||||
|
slot.disconnect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,14 +139,14 @@ 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{});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
caps.emplace(std::string{x, 0, eq}, std::string{x, eq+1, x.npos});
|
caps.emplace(std::string{x, 0, eq}, std::string{x, eq + 1, x.npos});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -150,17 +159,16 @@ auto RegistrationThread::on_msg_cap_ls(IrcMsg const& msg) -> void
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto RegistrationThread::start(
|
auto RegistrationThread::start(
|
||||||
Connection& connection,
|
Connection &connection,
|
||||||
const Settings &settings,
|
const Settings &settings,
|
||||||
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();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
class RegistrationThread : public std::enable_shared_from_this<RegistrationThread>
|
class RegistrationThread : public std::enable_shared_from_this<RegistrationThread>
|
||||||
{
|
{
|
||||||
Connection& connection_;
|
Connection &connection_;
|
||||||
const Settings &settings_;
|
const Settings &settings_;
|
||||||
std::shared_ptr<SelfThread> self_;
|
std::shared_ptr<SelfThread> self_;
|
||||||
|
|
||||||
@ -23,21 +23,21 @@ 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;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RegistrationThread(
|
RegistrationThread(
|
||||||
Connection& connection_,
|
Connection &connection_,
|
||||||
const Settings &,
|
const Settings &,
|
||||||
std::shared_ptr<SelfThread> self
|
std::shared_ptr<SelfThread> self
|
||||||
);
|
);
|
||||||
|
|
||||||
static auto start(
|
static auto start(
|
||||||
Connection& connection,
|
Connection &connection,
|
||||||
const Settings &,
|
const Settings &,
|
||||||
std::shared_ptr<SelfThread> self
|
std::shared_ptr<SelfThread> self
|
||||||
) -> std::shared_ptr<RegistrationThread>;
|
) -> std::shared_ptr<RegistrationThread>;
|
||||||
|
@ -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:
|
||||||
|
@ -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,42 +87,64 @@ 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) {
|
}
|
||||||
isupport_.emplace(entry.substr(0, cursor), entry.substr(cursor+1));
|
else if (const auto cursor = entry.find('='); cursor != entry.npos)
|
||||||
} else {
|
{
|
||||||
|
isupport_.emplace(entry.substr(0, cursor), entry.substr(cursor + 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
isupport_.emplace(entry, std::string{});
|
isupport_.emplace(entry, std::string{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SelfThread::start(Connection& connection) -> std::shared_ptr<SelfThread>
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@ struct IrcMsg;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Thread to track this connection's identity, and IRC state.
|
* @brief Thread to track this connection's identity, and IRC state.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class SelfThread
|
class SelfThread
|
||||||
{
|
{
|
||||||
Connection& connection_;
|
Connection &connection_;
|
||||||
|
|
||||||
std::string nickname_;
|
std::string nickname_;
|
||||||
std::string mode_;
|
std::string mode_;
|
||||||
@ -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)
|
||||||
static auto start(Connection&) -> std::shared_ptr<SelfThread>;
|
: connection_{connection}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
#define TOML_ENABLE_FORMATTERS 0
|
#define TOML_ENABLE_FORMATTERS 0
|
||||||
#include <toml++/toml.hpp>
|
#include <toml++/toml.hpp>
|
||||||
|
|
||||||
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{}),
|
||||||
.password = config["password"].value_or(std::string{}),
|
.password = config["password"].value_or(std::string{}),
|
||||||
.username = config["username"].value_or(std::string{}),
|
.username = config["username"].value_or(std::string{}),
|
||||||
.realname = config["realname"].value_or(std::string{}),
|
.realname = config["realname"].value_or(std::string{}),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <istream>
|
#include <istream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct Settings
|
struct Settings
|
||||||
{
|
{
|
||||||
@ -17,6 +17,5 @@ struct Settings
|
|||||||
std::string sasl_authzid;
|
std::string sasl_authzid;
|
||||||
std::string sasl_password;
|
std::string sasl_password;
|
||||||
|
|
||||||
static auto from_stream(std::istream & in) -> Settings;
|
static auto from_stream(std::istream &in) -> Settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
85
snote.cpp
85
snote.cpp
@ -17,20 +17,19 @@ 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}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
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: ([^ ]+) \(([^@ ]+)@([^) ]+)\) \[(.*)\] \{([^ ]*)\} <([^ ]*)> \[(.*)\]$)"},
|
||||||
|
|
||||||
@ -77,10 +76,10 @@ SnotePattern static const patterns[] =
|
|||||||
"^\x02([^ ]+)\x02 set vhost ([^ ]+) on the \x02MARKED\x02 account ([^ ]+).$"},
|
"^\x02([^ ]+)\x02 set vhost ([^ ]+) on the \x02MARKED\x02 account ([^ ]+).$"},
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@ -93,21 +92,20 @@ static auto setup_database() -> hs_database_t*
|
|||||||
ids.push_back(i);
|
ids.push_back(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
hs_database_t* db;
|
hs_database_t *db;
|
||||||
hs_compile_error *error;
|
hs_compile_error *error;
|
||||||
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)};
|
}
|
||||||
}
|
case HS_SUCCESS:
|
||||||
case HS_SUCCESS:
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
abort();
|
||||||
abort();
|
|
||||||
}
|
}
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
@ -118,7 +116,7 @@ SnoteCore::SnoteCore()
|
|||||||
{
|
{
|
||||||
db_.reset(setup_database());
|
db_.reset(setup_database());
|
||||||
|
|
||||||
hs_scratch_t* scratch = nullptr;
|
hs_scratch_t *scratch = nullptr;
|
||||||
if (HS_SUCCESS != hs_alloc_scratch(db_.get(), &scratch))
|
if (HS_SUCCESS != hs_alloc_scratch(db_.get(), &scratch))
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
@ -128,30 +126,29 @@ 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
|
scratch_.get(),
|
||||||
scratch_.get(),
|
CCallback<decltype(cb)>::invoke, &cb
|
||||||
CCallback<decltype(cb)>::invoke, &cb
|
);
|
||||||
);
|
|
||||||
|
|
||||||
switch (scan_result)
|
switch (scan_result)
|
||||||
{
|
{
|
||||||
@ -159,9 +156,8 @@ 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,14 +166,15 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [regex, message] = std::get<0>(components_);
|
auto [regex, message] = std::get<0>(components_);
|
||||||
auto& results = components_.emplace<1>();
|
auto &results = components_.emplace<1>();
|
||||||
if (not std::regex_match(message.begin(), message.end(), results, regex))
|
if (not std::regex_match(message.begin(), message.end(), results, regex))
|
||||||
{
|
{
|
||||||
// something went wrong - hyperscan disagrees with std::regex
|
// something went wrong - hyperscan disagrees with std::regex
|
||||||
@ -186,7 +183,7 @@ auto SnoteMatch::get_results() -> std::match_results<std::string_view::const_ite
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SnoteCore::DbDeleter::operator()(hs_database_t * db) const -> void
|
auto SnoteCore::DbDeleter::operator()(hs_database_t *db) const -> void
|
||||||
{
|
{
|
||||||
if (HS_SUCCESS != hs_free_database(db))
|
if (HS_SUCCESS != hs_free_database(db))
|
||||||
{
|
{
|
||||||
@ -194,7 +191,7 @@ auto SnoteCore::DbDeleter::operator()(hs_database_t * db) const -> void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SnoteCore::ScratchDeleter::operator()(hs_scratch_t * scratch) const -> void
|
auto SnoteCore::ScratchDeleter::operator()(hs_scratch_t *scratch) const -> void
|
||||||
{
|
{
|
||||||
if (HS_SUCCESS != hs_free_scratch(scratch))
|
if (HS_SUCCESS != hs_free_scratch(scratch))
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user