1
0
mirror of https://github.com/anope/anope.git synced 2026-07-05 03:13:13 +02:00

Compare commits

...

22 Commits

Author SHA1 Message Date
dependabot[bot] c784f08002 Bump actions/cache from 5 to 6
Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-01 20:20:23 +01:00
dependabot[bot] 6de2ae0046 Bump actions/checkout from 6 to 7
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-01 20:19:59 +01:00
Sadie Powell c26f742662 Run irctest on GitHub Actions.
[skip alpine ci]
[skip ubuntu ci]
[skip windows ci]
2026-06-29 17:45:04 +01:00
Sadie Powell e3bff85339 Disable unity builds for targets with only one source file. 2026-06-29 17:17:43 +01:00
TehPeGaSuS 5819c1fcf3 Document the ns_flexible and ns_monospace options. 2026-06-29 12:21:06 +01:00
Sadie Powell 4b8f073cb7 Improve configuring paths. 2026-06-29 01:31:55 +01:00
Kufat 67026b184d Enhance Atheme database import functionality. 2026-06-29 01:28:59 +01:00
Sadie Powell b49f984598 Improve support for versioning builds.
- Don't increment the build id when doing reproducible builds.
- Generate the build date in build.h so it always gets updated.
2026-06-17 22:52:14 +01:00
Sadie Powell b9cacf1d0f Fix HostServ request timeouts for DNS validated vhosts. 2026-06-14 18:16:43 +01:00
Sadie Powell 09d0bd6987 Fix the bcrypt/md5 vendored libraries not being marked as extern C. 2026-06-14 17:45:06 +01:00
Sadie Powell f150ee857f Build vendored libraries as static libraries and link against them.
This avoids rebuilding code we've already built.
2026-06-14 12:45:18 +01:00
Sadie Powell d60c80a4a3 Build with -fPIC. 2026-06-14 12:44:49 +01:00
Sadie Powell 44b7493eb1 Add support for HostServ request cooldowns. 2026-06-13 23:53:16 +01:00
Sadie Powell a03e765172 Fix a crash when FindFromService can not find a command.
Closes #583.
2026-06-12 19:23:39 +01:00
Sadie Powell 17cd10ef21 Fix a crashed caused by invalidation of an iterator in os_mode.
Closes #582.
2026-06-12 10:47:58 +01:00
Sadie Powell ca8fcbe119 Allow protocol modules to apply tags to sent messages.
This is useful because for some IRCv3 specifications we need to
apply tags to all messages and its annoying to have to do this
inline when sending each message.
2026-06-10 09:14:09 +01:00
Sadie Powell b9d0762f2b Add a function for building an ISO 8601 timestamp. 2026-06-10 09:14:09 +01:00
Sadie Powell 4878b1787e Only send tags on InspIRCd when we are done syncing. 2026-06-09 23:29:35 +01:00
Sadie Powell 0163d92b1d Fix showing tags in the protocoldebug output for sent messages. 2026-06-09 23:23:18 +01:00
Sadie Powell a861a059f6 Fix the config parser using int for values that can not be negative. 2026-06-08 15:41:00 +01:00
Sadie Powell 90da25f84f Add Block::GetBlocks which returns an iterable list of blocks. 2026-06-08 15:39:28 +01:00
Sadie Powell ab362c9828 Pascalize some typedefs in Configuration::Block. 2026-06-08 14:17:30 +01:00
53 changed files with 910 additions and 258 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
container: alpine:latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install dependencies
run: |-
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
if: "!contains(github.event.head_commit.message, '[skip ubuntu ci]')"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Install dependencies
run: |-
+3 -3
View File
@@ -18,7 +18,7 @@ jobs:
CONAN_FILE: ${{ github.workspace }}\src\win32\conanfile.txt
CONAN_HOME: ${{ github.workspace }}\build\conan
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Setup MSVC
uses: TheMrMilchmann/setup-msvc-dev@v4
@@ -45,7 +45,7 @@ jobs:
- name: Try to restore libraries from the cache
if: github.event_name != 'release'
uses: actions/cache/restore@v5
uses: actions/cache/restore@v6
id: library-cache
with:
key: Conan VS${{ env.VisualStudioVersion }} ${{ env.BUILD_TYPE }} ${{ hashFiles(env.CONAN_FILE) }}
@@ -60,7 +60,7 @@ jobs:
- name: Save libraries to the cache
if: steps.library-cache.outputs.cache-hit != 'true' && github.event_name != 'release'
uses: actions/cache/save@v5
uses: actions/cache/save@v6
with:
key: ${{ steps.library-cache.outputs.cache-primary-key }}
path: ${{ env.CONAN_HOME }}\p
+223
View File
@@ -0,0 +1,223 @@
name: irctest
# Last checked: 2026-06-29
env:
INSPIRCD_REF: v4.11.0 # 2026-06-06
IRCTEST_REF: ff6ef19d96cacd79ea8033dda2cf660fe5cdba52 # 2026-06-20
SOLANUM_REF: a17cc145a7de564e49b84807d335289f30b12d00 # 2026-06-23
UNREALIRCD_REF: 1250b7f014dbbe1dae13138dbef6e52c6dcd5557 # 2026-05-15 (6.2.5)
on:
pull_request:
push:
schedule:
- cron: 0 0 * * 0
workflow_dispatch:
jobs:
check-skip:
if: "!contains(github.event.head_commit.message, '[skip irctest ci]')"
runs-on: ubuntu-latest
steps:
- run: true # Avoids skip check duplication.
build-anope:
needs:
- check-skip
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Build
run: |-
sudo apt-get install build-essential cmake --assume-yes
cmake \
-B "${{ github.workspace }}/build" \
-D "CMAKE_UNITY_BUILD=ON" \
-D "CMAKE_UNITY_BUILD_BATCH_SIZE=0" \
-D "INSTDIR=$HOME/software/anope" \
-G "Ninja" \
-S "${{ github.workspace }}"
ninja -C "${{ github.workspace }}/build" install
- name: Make artifact
run: |-
tar \
--auto-compress \
--create \
--file installed-anope.tar.gz \
$HOME/software/anope
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: installed-anope
path: installed-anope.tar.gz
retention-days: 1
build-inspircd:
needs:
- check-skip
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
with:
ref: ${{ env.INSPIRCD_REF }}
repository: inspircd/inspircd
- name: Build
run: |-
sudo apt-get install build-essential --assume-yes
./configure \
--development \
--disable-auto-extras \
--prefix $HOME/software/inspircd
export CXXFLAGS="-DINSPIRCD_UNLIMITED_MAINLOOP"
make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) install
- name: Make artifact
run: |-
tar \
--auto-compress \
--create \
--file installed-inspircd.tar.gz \
$HOME/software/inspircd
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: installed-inspircd
path: installed-inspircd.tar.gz
retention-days: 1
build-solanum:
needs:
- check-skip
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
with:
ref: ${{ env.SOLANUM_REF }}
repository: solanum-ircd/solanum
- name: Build
run: |-
sudo apt-get install build-essential libltdl-dev --assume-yes
./autogen.sh
./configure \
--enable-fhs-paths \
--prefix $HOME/software/solanum
make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) install
- name: Make artifact
run: |-
tar \
--auto-compress \
--create \
--file installed-solanum.tar.gz \
$HOME/software/solanum
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: installed-solanum
path: installed-solanum.tar.gz
retention-days: 1
build-unrealircd:
needs:
- check-skip
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
with:
ref: ${{ env.UNREALIRCD_REF }}
repository: unrealircd/unrealircd
- name: Build
run: |-
sudo apt-get install build-essential libssl-dev --assume-yes
wget --output-document src/modules/third/metadata2.c https://raw.githubusercontent.com/progval/unrealircd-contrib/metadata2/files/metadata2.c
echo "BASEPATH=$HOME/software/unrealircd" >> config.settings
echo "GEOIP=none" >> config.settings
yes "" | ./Config
make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) install
- name: Make artifact
run: |-
tar \
--auto-compress \
--create \
--file installed-unrealircd.tar.gz \
$HOME/software/unrealircd
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: installed-unrealircd
path: installed-unrealircd.tar.gz
retention-days: 1
test:
runs-on: ubuntu-latest
needs:
- build-anope
- build-inspircd
- build-solanum
- build-unrealircd
steps:
- name: Download Anope artifact
uses: actions/download-artifact@v8
with:
name: installed-anope
- name: Download server artifact
uses: actions/download-artifact@v8
with:
name: installed-${{ matrix.server }}
- name: Unpack artifacts
run: |-
for ARCHIVE in installed-anope installed-${{ matrix.server }}
do
tar \
--directory / \
--extract \
--file ${ARCHIVE}.tar.gz
done
for SOFTWARE in $HOME/software/*
do
echo "Adding ${SOFTWARE} to the PATH"
export PATH=$SOFTWARE/bin:$SOFTWARE/sbin:$PATH
done
echo PATH=$PATH >> $GITHUB_ENV
- name: Checkout irctest
uses: actions/checkout@v7
with:
ref: ${{ env.IRCTEST_REF }}
repository: progval/irctest
- name: Install irctest dependencies
run: |-
sudo apt-get install faketime
pip3 install --requirement requirements.txt pytest-xdist pytest-timeout
- name: Run irctest
env:
IRCTEST_DEBUG_LOGS: ${{ runner.debug }}
PYTEST_ARGS: --color=yes --verbose
run: |-
make ${{ matrix.server}}-anope
strategy:
fail-fast: false
matrix:
server:
- inspircd
- solanum
- unrealircd
+40 -29
View File
@@ -1,8 +1,8 @@
# This usage of CMake requires at least version 3.20
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
# Set the project as C++ primarily
project(Anope CXX)
# Set the project as C++ primarily with C for the vendored libraries
project(Anope CXX C)
# Force the locale to C for later uses of things like gcc so the messages come up in English, not the user's default language
set(ENV{LC_ALL} C)
@@ -25,6 +25,7 @@ include(CheckFunctionExists)
include(CheckTypeSize)
include(CheckLibraryExists)
include(CheckCXXCompilerFlag)
include(CheckPIESupported)
if(NOT WIN32)
include(FindPkgConfig)
@@ -40,6 +41,10 @@ if(EXTRA_LIBS)
link_directories(${EXTRA_LIBS})
endif()
# Enable -fPIC for all targets.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
check_pie_supported()
# Find gettext
find_package(Gettext)
find_package(Intl)
@@ -144,28 +149,37 @@ elseif(NOT CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/anope-${VERSION_MAJOR}-${VERSION_MINOR}")
endif()
function(configure_path VARIABLE DEFAULT)
if(NOT DEFINED ${VARIABLE})
set(${VARIABLE} ${DEFAULT} CACHE PATH "Location of the ${DEFAULT} directory")
endif()
if(WIN32)
set(ABSOLUTE_${VARIABLE} ${${VARIABLE}})
else()
cmake_path(
APPEND CMAKE_INSTALL_PREFIX "${${VARIABLE}}"
OUTPUT_VARIABLE ABSOLUTE_${VARIABLE}
)
cmake_path(NORMAL_PATH ABSOLUTE_${VARIABLE})
endif()
set(ABSOLUTE_${VARIABLE} ${ABSOLUTE_${VARIABLE}} CACHE INTERNAL "" FORCE)
install(
DIRECTORY
DESTINATION ${${VARIABLE}}
)
endfunction()
# Set default paths for various directories if not already defined
if(NOT BIN_DIR)
set(BIN_DIR "bin")
endif()
if(NOT DATA_DIR)
set(DATA_DIR "data")
endif()
if(NOT DOC_DIR)
set(DOC_DIR "doc")
endif()
if(NOT CONF_DIR)
set(CONF_DIR "conf")
endif()
if(NOT MODULE_DIR)
set(MODULE_DIR "modules")
endif()
if(NOT LOCALE_DIR)
set(LOCALE_DIR "locale")
endif()
if(NOT LOG_DIR)
set(LOG_DIR "logs")
endif()
configure_path(BIN_DIR "bin")
configure_path(DATA_DIR "data")
configure_path(DOC_DIR "doc")
configure_path(CONF_DIR "conf")
configure_path(MODULE_DIR "modules")
configure_path(LOCALE_DIR "locale")
configure_path(LOG_DIR "logs")
# Version number processing
# Find all lines in src/version.sh that start with VERSION_
@@ -218,12 +232,9 @@ set(SERVICES_BINARY "$<TARGET_FILE:${PROGRAM_NAME}>")
cmake_path(GET SERVICES_BINARY FILENAME SERVICES_BINARY)
# At install time, create the following additional directories
file(REAL_PATH ${DATA_DIR} ABSOLUTE_DATA_DIR BASE_DIRECTORY ${CMAKE_INSTALL_PREFIX})
file(REAL_PATH ${LOG_DIR} ABSOLUTE_LOG_DIR BASE_DIRECTORY ${CMAKE_INSTALL_PREFIX})
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ABSOLUTE_DATA_DIR}/backups\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ABSOLUTE_LOG_DIR}\")")
install(DIRECTORY DESTINATION "${DATA_DIR}/backups")
if(WIN32)
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ABSOLUTE_DATA_DIR}/runtime\")")
install(DIRECTORY DESTINATION "${DATA_DIR}/runtime")
endif()
# On non-Windows platforms, if RUNGROUP is set, change the permissions of the below directories, as well as the group of the data directory
if(NOT WIN32 AND RUNGROUP)
@@ -238,7 +249,6 @@ if(WIN32)
)
endif()
file(REAL_PATH ${MODULE_DIR} ABSOLUTE_MODULE_DIR BASE_DIRECTORY ${CMAKE_INSTALL_PREFIX})
install(CODE "file(REMOVE_RECURSE \"$ENV{DESTDIR}${ABSOLUTE_MODULE_DIR}\")")
# Only process the CPack section if we have CPack
@@ -289,6 +299,7 @@ endif()
add_subdirectory(data)
add_subdirectory(docs)
add_subdirectory(language)
add_subdirectory(vendor)
add_subdirectory(src)
add_subdirectory(modules)
add_subdirectory(include)
+13
View File
@@ -201,6 +201,19 @@ module
{
name = "hs_request"
/*
* How long after a requested vhost is activated does a user have to wait
* before they can request a new vhost. Defaults to 24 hours.
*/
#activationcooldown = 24h
/*
* How long after a requested vhost is rejected does a user have to wait
* before they can request a new vhost. Defaults to 24 hours.
*/
#rejectioncooldown = 24h
/*
* If set, Anope will send a memo to the user requesting a vhost when it's been
* approved or rejected.
+4
View File
@@ -120,6 +120,10 @@ module
* - ns_keep_modes: Enables keepmodes, which retains user modes across sessions
* - protect: Protects the registered nickname from use by unidentified users.
* - ns_stats: Enable Chanstats for newly registered nicks
* - ns_flexible: Set FLEXIBLE as layout for messages from services (can not be set at the same
* time as ns_monospace)
* - ns_monospace: Set MONOSPACE as layout for messages from services (can not be set as the same
* time as ns_flexible)
*
* This directive is optional, if left blank, the options will default to memo_signon, and
* memo_receive. If you really want no defaults, use "none" by itself as the option.
-4
View File
@@ -362,10 +362,6 @@ namespace Anope
template<typename T>
using unordered_map = std::unordered_map<string, T, hash_ci, compare>;
#if !REPRODUCIBLE_BUILD
static const char *const compiled = __TIME__ " " __DATE__;
#endif
/** The time Anope started.
*/
extern CoreExport time_t StartTime;
+13 -10
View File
@@ -18,6 +18,7 @@
#include "regchannel.h"
#include "users.h"
#include "opertype.h"
#include "miscutils.h"
namespace Configuration
{
@@ -26,14 +27,15 @@ namespace Configuration
friend class Configuration::Conf;
public:
typedef Anope::map<Anope::string> item_map;
typedef Anope::multimap<Block> block_map;
typedef Anope::map<Anope::string> ItemMap;
typedef Anope::multimap<Block> BlockMap;
typedef Anope::iterator_range<BlockMap::const_iterator> BlockList;
private:
Anope::string name;
item_map items;
block_map blocks;
int linenum;
ItemMap items;
BlockMap blocks;
unsigned linenum;
/* Represents a missing tag. */
static Block EmptyBlock;
@@ -41,9 +43,10 @@ namespace Configuration
public:
Block(const Anope::string &);
const Anope::string &GetName() const;
int CountBlock(const Anope::string &name) const;
const Block &GetBlock(const Anope::string &name, int num = 0) const;
Block *GetMutableBlock(const Anope::string &name, int num = 0);
size_t CountBlock(const Anope::string &name) const;
BlockList GetBlocks(const Anope::string &name) const;
const Block &GetBlock(const Anope::string &name, size_t num = 0) const;
Block *GetMutableBlock(const Anope::string &name, size_t num = 0);
template<typename T> T Get(const Anope::string &tag, const Anope::string &def = "") const
{
@@ -51,7 +54,7 @@ namespace Configuration
}
bool Set(const Anope::string &tag, const Anope::string &value);
const item_map &GetItems() const;
const ItemMap &GetItems() const;
};
template<> CoreExport const Anope::string Block::Get(const Anope::string &tag, const Anope::string &def) const;
@@ -85,7 +88,7 @@ namespace Configuration
{
private:
/** Replaces defined variables within a string. */
Anope::string ReplaceVars(const Anope::string &str, const File &file, int linenumber);
Anope::string ReplaceVars(const Anope::string &str, const File &file, unsigned linenumber);
public:
/* options:readtimeout */
+87
View File
@@ -0,0 +1,87 @@
// Anope IRC Services <https://www.anope.org/>
//
// Copyright (C) 2003-2026 Anope Contributors
//
// Anope is free software. You can use, modify, and/or distribute it under the
// terms of version 2 of the GNU General Public License. See docs/LICENSE.txt
// for the complete terms of this license and docs/AUTHORS.txt for a list of
// contributors.
//
// Based on the original code of Epona by Lara
// Based on the original code of Services by Andy Church
//
// SPDX-License-Identifier: GPL-2.0-only
#pragma once
#include <iterator>
#include <utility>
namespace Anope
{
template <typename Iterator>
class iterator_range;
/** Returns a range containing all elements equivalent to \p value.
* @param collection The collection to search within.
* @param value The value to search for.
*/
template <typename Collection, typename Value>
auto equal_range(const Collection& collection, const Value& value)
{
return iterator_range(collection.equal_range(value));
}
/** Returns a range representing a reverse iterator for the specified colleciton.
* @param collection The collection to create a reverse iterator for.
*/
template <typename Collection>
auto reverse_range(const Collection& collection)
{
return iterator_range(collection.rbegin(), collection.rend());
}
}
/** Represents a range of iterators. */
template <typename Iterator>
class Anope::iterator_range final
{
private:
/** An iterator which points to the start of the range. */
const Iterator begini;
/* An iterator which points to one past the end of the range. */
const Iterator endi;
public:
/** Initialises a new iterator range with the specified iterators.
* @param begin An iterator which points to the start of the range.
* @param end An iterator which points to one past the end of the range.
*/
explicit iterator_range(Iterator begin, Iterator end)
: begini(begin)
, endi(end)
{
}
/** Initialises a new iterator range from a pair of iterators.
* @param range A pair of iterators in the format [first, last).
*/
explicit iterator_range(std::pair<Iterator, Iterator> range)
: begini(range.first)
, endi(range.second)
{
}
/** Determines whether the iterator range is empty. */
bool empty() const { return begini == endi; }
/** Retrieves an iterator which points to the start of the range. */
const Iterator& begin() const { return begini; }
/** Retrieves an iterator which points to one past the end of the range. */
const Iterator& end() const { return endi; }
/** Retrieves the number of hops within the iterator range. */
typename std::iterator_traits<Iterator>::difference_type count() const { return std::distance(begini, endi); }
};
+47
View File
@@ -0,0 +1,47 @@
// Anope IRC Services <https://www.anope.org/>
//
// Copyright (C) 2003-2026 Anope Contributors
//
// Anope is free software. You can use, modify, and/or distribute it under the
// terms of version 2 of the GNU General Public License. See docs/LICENSE.txt
// for the complete terms of this license and docs/AUTHORS.txt for a list of
// contributors.
//
// Based on the original code of Epona by Lara
// Based on the original code of Services by Andy Church
//
// SPDX-License-Identifier: GPL-2.0-only
#pragma once
#define NICKSERV_AJOIN_LIST_EXT "ajoinlist"
struct AJoinEntry;
struct AJoinList
: Serialize::Checker<std::vector<AJoinEntry *> >
{
AJoinList(Extensible *) : Serialize::Checker<std::vector<AJoinEntry *> >("AJoinEntry") { }
virtual ~AJoinList() = default;
};
struct AJoinEntry final
: Serializable
{
Serialize::Reference<NickCore> owner;
Anope::string channel;
Anope::string key;
AJoinEntry(Extensible *) : Serializable("AJoinEntry") { }
~AJoinEntry() override
{
auto *channels = owner->GetExt<AJoinList>(NICKSERV_AJOIN_LIST_EXT);
if (channels)
{
auto it = std::find((*channels)->begin(), (*channels)->end(), this);
if (it != (*channels)->end())
(*channels)->erase(it);
}
}
};
+8
View File
@@ -58,6 +58,14 @@ public:
*/
virtual bool Parse(const Anope::string &message, Anope::map<Anope::string> &tags, Anope::string &source, Anope::string &command, std::vector<Anope::string> &params);
/* Populates the tags that should be sent on all messages.
* @param tags The location to store tags.
* @param source Source of the message.
* @param command Command name.
* @param params Any extra parameters.
*/
virtual void PopulateTags(Anope::map<Anope::string> &tags, const MessageSource &source, const Anope::string &command, const std::vector<Anope::string> &params);
/* Formats an outgoing message so it can be sent to the IRC server.
* @param message The location to store the formatted message.
* @param tags IRCv3 message tags.
+6
View File
@@ -79,6 +79,12 @@ namespace Anope
*/
extern CoreExport Anope::string FormatCTCP(const Anope::string &name, const Anope::string &body = "");
/** Formats a date/time as an IS0 8601 timestamp.
* @param ts UNIX timestamp to format.
* @param ms Number of milliseconds to format.
*/
extern CoreExport Anope::string FormatISO8601(time_t ts, unsigned long long ms);
/** Parses a CTCP message received from a client.
* @param text The raw message to parse.
* @param name The location to store the name of the CTCP.
+17 -2
View File
@@ -18,6 +18,8 @@
#include <sstream>
#include <map>
#include "sysconf.h"
static std::string get_git_hash(const std::string &git_dir)
{
std::fstream fd;
@@ -78,14 +80,18 @@ static bool read_version_sh(const std::string &version_sh, std::map<std::string,
static bool write_build_h(const std::string &buildh, const std::string &git_version)
{
std::fstream fd(buildh.c_str(), std::ios::in);
std::fstream fd;
std::string build = "#define BUILD 1";
#if !REPRODUCIBLE_BUILD
fd.open(buildh.c_str(), std::ios::in);
if (fd.is_open())
{
for (std::string filebuf; getline(fd, filebuf);)
{
if (!filebuf.find("#define BUILD"))
if (!filebuf.find("#define BUILD\t"))
{
size_t tab = filebuf.find(' ');
@@ -101,6 +107,8 @@ static bool write_build_h(const std::string &buildh, const std::string &git_vers
}
fd.clear();
#endif
fd.open(buildh.c_str(), std::ios::out);
if (!fd.is_open())
{
@@ -111,6 +119,13 @@ static bool write_build_h(const std::string &buildh, const std::string &git_vers
fd << "/* This file is automatically generated by version.cpp - do not edit it! */" << std::endl
<< "#pragma once" << std::endl
<< build << std::endl;
#if !REPRODUCIBLE_BUILD
auto time_now = time(NULL);
auto *local_tm = localtime(&time_now);
char buffer[100];
strftime(buffer, sizeof(buffer), "%a %b %e %Y %T %Z", local_tm);
fd << "#define BUILD_DATE \"" << buffer << "\"" << std::endl;
#endif
if (!git_version.empty())
fd << "#define VERSION_GIT \"" << git_version << "\"" << std::endl;
fd.close();
+7 -1
View File
@@ -50,7 +50,7 @@ macro(inline_cmake TARGET FILE)
set(CODE "${CODE}\n${CLEAN_LINE}")
endif()
elseif(LINE MATCHES "^/// BEGIN CMAKE$")
message(STATUS "Executing inline CMake code for ${TARGET}")
message(DEBUG "Executing inline CMake code for ${TARGET}")
set(IN_CODE ON)
endif()
endforeach()
@@ -77,6 +77,12 @@ macro(build_module SRC MODULE_SRC)
PREFIX ""
SUFFIX ""
)
list(LENGTH MODULE_SRC MODULE_SRC_COUNT)
if(MODULE_SRC_COUNT LESS_EQUAL 1)
set_target_properties(${SO} PROPERTIES
UNITY_BUILD OFF
)
endif()
add_dependencies(${SO} ${PROGRAM_NAME})
if(HAVE_LOCALIZATION)
add_dependencies(${SO} module_language)
+1 -3
View File
@@ -963,10 +963,8 @@ public:
{
defaultLevels.clear();
for (int i = 0; i < conf.CountBlock("privilege"); ++i)
for (const auto &[_, priv] : conf.GetBlocks("privilege"))
{
const auto &priv = conf.GetBlock("privilege", i);
const Anope::string &pname = priv.Get<const Anope::string>("name");
Privilege *p = PrivilegeManager::FindPrivilege(pname);
+1 -5
View File
@@ -299,11 +299,7 @@ public:
{
Anope::map<Anope::string> tags;
if (timestamp)
{
char timebuf[32];
strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S.000Z", gmtime(&message->when));
tags["time"] = timebuf;
}
tags["time"] = Anope::FormatISO8601(message->when, 0);
if (u->ShouldPrivmsg())
IRCD->SendContextPrivmsg(c->ci->WhoSends(), u, c, message->message, tags);
+1 -3
View File
@@ -661,10 +661,8 @@ public:
{
defaultFlags.clear();
for (int i = 0; i < conf.CountBlock("privilege"); ++i)
for (const auto &[_, priv] : conf.GetBlocks("privilege"))
{
const auto &priv = conf.GetBlock("privilege", i);
const Anope::string &pname = priv.Get<const Anope::string>("name");
Privilege *p = PrivilegeManager::FindPrivilege(pname);
+1 -3
View File
@@ -331,10 +331,8 @@ public:
const auto &block = conf.GetModule(this);
defaults.clear();
for (int i = 0; i < block.CountBlock("default"); ++i)
for (const auto &[_, def] : block.GetBlocks("default"))
{
const auto &def = block.GetBlock("default", i);
LogDefault ld;
ld.service = def.Get<const Anope::string>("service");
+1 -3
View File
@@ -999,10 +999,8 @@ public:
{
modes.clear();
for (int i = 0; i < conf.CountBlock("command"); ++i)
for (const auto &[_, block] : conf.GetBlocks("command"))
{
const auto &block = conf.GetBlock("command", i);
const Anope::string &cname = block.Get<const Anope::string>("name"),
&cmd = block.Get<const Anope::string>("command");
+2 -2
View File
@@ -268,9 +268,9 @@ public:
void OnReload(Configuration::Conf &conf) override
{
command_data.clear();
for (int i = 0; i < conf.CountBlock("command"); ++i)
for (const auto &[_, block] : conf.GetBlocks("command"))
{
const auto &block = conf.GetBlock("command", i);
if (block.Get<const Anope::string>("command") != "chanserv/set/misc")
continue;
+2 -4
View File
@@ -688,9 +688,8 @@ public:
order.clear();
permissions.clear();
for (int i = 0; i < conf.CountBlock("privilege"); ++i)
for (const auto &[_, block] : conf.GetBlocks("privilege"))
{
const auto &block = conf.GetBlock("privilege", i);
const Anope::string &pname = block.Get<const Anope::string>("name");
Privilege *p = PrivilegeManager::FindPrivilege(pname);
@@ -704,9 +703,8 @@ public:
permissions[xop].push_back(pname);
}
for (int i = 0; i < conf.CountBlock("command"); ++i)
for (const auto &[_, block] : conf.GetBlocks("command"))
{
const auto &block = conf.GetBlock("command", i);
const Anope::string &cname = block.Get<const Anope::string>("name"),
&cserv = block.Get<const Anope::string>("command");
if (cname.empty() || cserv != "chanserv/xop")
+175 -24
View File
@@ -12,6 +12,7 @@
//
// SPDX-License-Identifier: GPL-2.0-only
#include <algorithm>
#include "module.h"
#include "modules/botserv/badwords.h"
@@ -21,6 +22,7 @@
#include "modules/chanserv/mode.h"
#include "modules/hostserv/request.h"
#include "modules/info.h"
#include "modules/nickserv/ajoin.h"
#include "modules/nickserv/cert.h"
#include "modules/operserv/forbid.h"
#include "modules/operserv/news.h"
@@ -73,10 +75,10 @@ public:
}
// Retrieves the remaining data in the row.
Anope::string GetRemaining()
Anope::string GetRemaining(bool allow_empty = false)
{
auto remaining = stream.GetRemaining();
if (remaining.empty())
if (remaining.empty() && !allow_empty)
error++;
return remaining;
}
@@ -120,14 +122,49 @@ struct ModeLockData final
}
};
struct FounderSuccessorCandidate
{
// Flags ranked from founder down, for successor candidate ranking
static const Anope::string FLAG_PRIORITY;
NickCore* nc;
size_t priority;
time_t mtime;
FounderSuccessorCandidate()
: nc(nullptr)
, priority(99) // arbitrary; for log readability
, mtime(std::numeric_limits<std::time_t>::max())
{
}
FounderSuccessorCandidate(NickCore *c, const Anope::string &flags, time_t m)
: nc(c)
, priority(FLAG_PRIORITY.find_first_of(flags))
, mtime(m)
{
}
bool operator<(const FounderSuccessorCandidate& other)
{
return std::tie(priority, mtime) < std::tie(other.priority, other.mtime);
}
FounderSuccessorCandidate& operator=(const FounderSuccessorCandidate& other) = default;
};
const Anope::string FounderSuccessorCandidate::FLAG_PRIORITY = "FSRsaOoHh";
struct ChannelData final
{
Anope::unordered_map<ChanServ::AutoKick *> akicks;
Anope::string bot;
FounderSuccessorCandidate founder_candidate;
Anope::string info_adder;
Anope::string info_message;
time_t info_ts = 0;
std::vector<ModeLockData> mlocks;
FounderSuccessorCandidate successor_candidate;
Anope::string suspend_by;
Anope::string suspend_reason;
time_t suspend_ts = 0;
@@ -135,6 +172,7 @@ struct ChannelData final
struct UserData final
{
Anope::map<Anope::string> ajoins;
Anope::string info_adder;
Anope::string info_message;
time_t info_ts = 0;
@@ -236,21 +274,40 @@ private:
{ "XL", &DBAtheme::HandleXL },
};
void ApplyAccess(Anope::string &in, char flag, Anope::string &out, std::initializer_list<const char*> privs)
static void RemoveAll(Anope::string& in, const Anope::string& unwanted)
{
for (const auto *priv : privs)
auto it = std::remove_if(in.begin(), in.end(), [&](char c){
return unwanted.find_first_of(c) != unwanted.npos;
});
in.erase(it, in.end());
}
static bool RemoveFirstOccurrence(Anope::string& in, char c)
{
auto pos = in.find(c);
if (pos != Anope::string::npos)
{
auto pos = in.find(flag);
if (pos != Anope::string::npos)
in.erase(pos, 1);
return true;
}
return false;
}
bool ApplyAccess(Anope::string &in, char flag, Anope::string &out, std::initializer_list<const char*> privs)
{
const bool flag_found = RemoveFirstOccurrence(in, flag);
if (flag_found)
{
for (const auto *priv : privs)
{
auto privchar = flags.find(priv);
if (privchar != flags.end())
{
out.push_back(privchar->second);
in.erase(pos, 1);
}
}
}
return flag_found;
}
void ApplyFlags(Extensible *ext, Anope::string &flags, char flag, const char *extname, bool extend = true)
@@ -609,16 +666,17 @@ private:
return false;
}
auto *data = chandata.Require(ci);
auto *nc = NickCore::Find(mask);
if (flags.find('b') != Anope::string::npos)
{
if (ChanServ::akick_service)
if (!ChanServ::akick_service)
{
Log(this) << "Unable to import channel akick for " << ci->name << " as cs_akick is not loaded";
return true;
}
auto *data = chandata.Require(ci);
if (nc)
data->akicks[mask] = ChanServ::akick_service->AddAKick(ci, setter, nc, "", modifiedtime, modifiedtime);
else
@@ -638,7 +696,6 @@ private:
ApplyAccess(flags, 'a', accessflags, { "AUTOPROTECT", "PROTECT", "PROTECTME" });
ApplyAccess(flags, 'e', accessflags, { "GETKEY", "NOKICK", "UNBANME" });
ApplyAccess(flags, 'f', accessflags, { "ACCESS_CHANGE" });
ApplyAccess(flags, 'F', accessflags, { "FOUNDER" });
ApplyAccess(flags, 'H', accessflags, { "AUTOHALFOP" });
ApplyAccess(flags, 'h', accessflags, { "HALFOP", "HALFOPME" });
ApplyAccess(flags, 'i', accessflags, { "INVITE" });
@@ -662,6 +719,35 @@ private:
ci->AddAccess(access);
}
// Atheme allows multiple founders and picks a successor based on rank if one is not explicitly assigned.
bool is_founder_candidate = RemoveFirstOccurrence(flags, 'F');
RemoveAll(flags, "SR");
FounderSuccessorCandidate current_candidate(nc, originalflags, modifiedtime);
if (nc && current_candidate < data->successor_candidate)
{
if (is_founder_candidate && current_candidate < data->founder_candidate)
{
Log(LOG_DEBUG) << ci->name << ": Demoting founder candidate ("
<< ( data->founder_candidate.nc ? data->founder_candidate.nc->display : "NONE" )
<< ", " << data->founder_candidate.priority
<< ") to successor; replacing with (" << current_candidate.nc->display
<< ", " << current_candidate.priority << ")";
data->successor_candidate = data->founder_candidate;
data->founder_candidate = current_candidate;
}
else
{
Log(LOG_DEBUG) << ci->name << ": Replacing successor candidate ("
<< ( data->successor_candidate.nc ? data->successor_candidate.nc->display : "NONE" )
<< ", " << data->successor_candidate.priority
<< ") with (" << current_candidate.nc->display
<< ", " << current_candidate.priority << ")";
data->successor_candidate = current_candidate;
}
}
if (flags != "+")
Log(this) << "Unable to convert channel access flags " << flags << " for " << mask << " on " << ci->name;
@@ -788,7 +874,7 @@ private:
return true;
}
auto *xl = new XLine(user + "@" + host, setby, settime + duration, reason);
auto *xl = new XLine(user + "@" + host, setby, duration ? settime + duration : 0, reason);
xl->id = id;
sglinemgr->AddXLine(xl);
return true;
@@ -879,6 +965,7 @@ private:
ci->last_used = used;
// No equivalent: elnv
RemoveFirstOccurrence(flags, 'v'); // verbose, com
ApplyFlags(ci, flags, 'h', "CS_NO_EXPIRE");
ApplyFlags(ci, flags, 'k', "KEEPTOPIC");
ApplyFlags(ci, flags, 'o', "NOAUTOOP");
@@ -985,6 +1072,10 @@ private:
if (akick != data->akicks.end())
akick->second->reason = value;
}
else if (key == "expires")
{
Log(this) << "Unable to set access expiration for " << mask << " on " << ci->name << ": unimplemented";
}
else
Log(this) << "Unknown channel access metadata for " << mask << " on " << ci->name << ": " << key << " = " << value;
@@ -1146,10 +1237,7 @@ private:
// MDU <display> <key> <value>
auto display = row.Get();
auto key = row.Get();
auto value = row.GetRemaining();
if (!row)
return row.LogError(this);
auto value = row.GetRemaining(true);
auto *nc = NickCore::Find(display);
if (!nc)
@@ -1160,7 +1248,17 @@ private:
auto *data = userdata.Require(nc);
if (key == "private:autojoin")
return true; // TODO
{
commasepstream autojoins(value, true);
for (Anope::string autojoin; autojoins.GetToken(autojoin); )
{
spacesepstream entry(autojoin);
Anope::string cname, ckey;
if (entry.GetToken(cname))
entry.GetToken(ckey);
data->ajoins[cname] = ckey;
}
}
else if (key == "private:doenforce")
data->protect = true;
else if (key == "private:enforcetime")
@@ -1189,6 +1287,12 @@ private:
data->info_adder = value;
else if (key == "private:mark:timestamp")
data->info_ts = Anope::Convert<time_t>(value, 0);
else if (key == "private:sendpass:sender")
return HandleIgnoreMetadata(nc->display, key, value);
else if (key == "private:sendpass:timestamp")
return HandleIgnoreMetadata(nc->display, key, value);
else if (key == "private:setpass:key")
return HandleIgnoreMetadata(nc->display, key, value);
else if (key == "private:swhois")
return HandleIgnoreMetadata(nc->display, key, value);
else if (key == "private:usercloak")
@@ -1410,6 +1514,7 @@ private:
ApplyPassword(nc, flags, pass);
// No equivalent: bglmNQrS
RemoveFirstOccurrence(flags, 'b'); // nick b flag is ephemeral, ignore
ApplyFlags(nc, flags, 'E', "PROTECT");
ApplyFlags(nc, flags, 'e', "MEMO_MAIL");
ApplyFlags(nc, flags, 'n', "NEVEROP");
@@ -1499,7 +1604,7 @@ private:
return true;
}
auto *xl = new XLine(nick, setby, settime + duration, reason);
auto *xl = new XLine(nick, setby, duration ? settime + duration : 0, reason);
xl->id = id;
sqlinemgr->AddXLine(xl);
return true;
@@ -1561,7 +1666,7 @@ private:
return true;
}
auto *xl = new XLine(real, setby, settime + duration, reason);
auto *xl = new XLine(real, setby, duration ? settime + duration : 0, reason);
xl->id = id;
snlinemgr->AddXLine(xl);
return true;
@@ -1584,9 +1689,8 @@ public:
const auto &modconf = conf.GetModule(this);
csmiscdata.clear();
for (auto idx = 0; idx < modconf.CountBlock("cs_set_misc"); ++idx)
for (const auto &[_, data] : modconf.GetBlocks("cs_set_misc"))
{
const auto &data = modconf.GetBlock("cs_set_misc", idx);
const auto &anope = data.Get<const Anope::string>("anope");
const auto &atheme = data.Get<const Anope::string>("atheme");
if (!anope.empty() && !atheme.empty())
@@ -1594,9 +1698,8 @@ public:
}
nsmiscdata.clear();
for (auto idx = 0; idx < modconf.CountBlock("ns_set_misc"); ++idx)
for (const auto &[_, data] : modconf.GetBlocks("ns_set_misc"))
{
const auto &data = modconf.GetBlock("ns_set_misc", idx);
const auto &anope = data.Get<const Anope::string>("anope");
const auto &atheme = data.Get<const Anope::string>("atheme");
if (!anope.empty() && !atheme.empty())
@@ -1604,9 +1707,8 @@ public:
}
flags.clear();
for (int i = 0; i < Config->CountBlock("privilege"); ++i)
for (const auto &[_, priv] : conf.GetBlocks("privilege"))
{
const auto &priv = Config->GetBlock("privilege", i);
const Anope::string &name = priv.Get<const Anope::string>("name");
const Anope::string &value = priv.Get<const Anope::string>("flag");
if (!name.empty() && !value.empty())
@@ -1692,6 +1794,55 @@ public:
if (!data)
continue;
if (!data->ajoins.empty())
{
auto *channels = nc->Require<AJoinList>(NICKSERV_AJOIN_LIST_EXT);
if (channels)
{
for (const auto& ajoin : data->ajoins)
{
auto &channel = ajoin.first, &key = ajoin.second;
if (!key.empty())
{
Channel *c = Channel::Find(channel);
Anope::string k;
if (c && c->GetParam("KEY", k) && key != k)
{
Log(this) << "Skipping ajoin with incorrect key for channel " << channel
<< ", user " << nc->display;
continue;
}
}
if (!IRCD->IsChannelValid(channel))
{
Log(this) << "Invalid ajoin channel " << channel << " for " << nc->display;
}
else
{
const auto it = std::find_if((*channels)->cbegin(), (*channels)->cend(), [&](const AJoinEntry* a){
return a->channel == channel;
});
if (it != (*channels)->cend())
{
Log(this) << "Skipping duplicate ajoin channel" << channel << " for " << nc->display;
continue;
}
auto* entry = new AJoinEntry(nc);
entry->owner = nc;
entry->channel = channel;
entry->key = key;
(*channels)->push_back(entry); // ignore ajoinmax for non-disruptive migration
}
}
}
else
{
Log(this) << "Unable to convert autojoins for " << nc->display << " as ns_ajoin is not loaded";
}
}
if (!data->info_message.empty())
{
auto *oil = nc->Require<OperInfoList>("operinfo");
+5 -1
View File
@@ -12,10 +12,14 @@
//
// SPDX-License-Identifier: GPL-2.0-only
/// BEGIN CMAKE
/// target_link_libraries(${SO} PRIVATE "vendored_yyjson")
/// END CMAKE
#include <filesystem>
namespace fs = std::filesystem;
#include "yyjson/yyjson.c"
#include "yyjson/yyjson.h"
#include "module.h"
+1 -2
View File
@@ -1095,9 +1095,8 @@ public:
refresh = block.Get<int>("refresh", "3600");
std::vector<std::pair<Anope::string, short> > notify;
for (int i = 0; i < block.CountBlock("notify"); ++i)
for (const auto &[_, n] : block.GetBlocks("notify"))
{
const auto &n = block.GetBlock("notify", i);
auto nip = n.Get<Anope::string>("ip");
auto nport = n.Get<short>("port");
+3 -6
View File
@@ -126,9 +126,8 @@ public:
this->add_to_akill = block.Get<bool>("add_to_akill", "yes");
this->blacklists.clear();
for (int i = 0; i < block.CountBlock("blacklist"); ++i)
for (const auto &[_, bl] : block.GetBlocks("blacklist"))
{
const auto &bl = block.GetBlock("blacklist", i);
Blacklist blacklist;
blacklist.name = bl.Get<Anope::string>("name");
@@ -137,9 +136,8 @@ public:
blacklist.bantime = bl.Get<time_t>("time", "4h");
blacklist.reason = bl.Get<Anope::string>("reason");
for (int j = 0; j < bl.CountBlock("reply"); ++j)
for (const auto &[_, reply] : block.GetBlocks("reply"))
{
const auto &reply = bl.GetBlock("reply", j);
Blacklist::Reply r;
r.code = reply.Get<int>("code");
@@ -153,9 +151,8 @@ public:
}
this->exempts.clear();
for (int i = 0; i < block.CountBlock("exempt"); ++i)
for (const auto &[_, bl] : block.GetBlocks("exempt"))
{
const auto &bl = block.GetBlock("exempt", i);
this->exempts.insert(bl.Get<Anope::string>("ip"));
}
}
+8 -1
View File
@@ -12,10 +12,17 @@
//
// SPDX-License-Identifier: GPL-2.0-only
/// BEGIN CMAKE
/// target_link_libraries(${SO} PRIVATE "vendored_bcrypt")
/// END CMAKE
#include <climits>
#include <random>
#include "bcrypt/crypt_blowfish.c"
extern "C"
{
#include "bcrypt/crypt_blowfish.h"
}
#include "module.h"
#include "modules/encryption.h"
+8 -1
View File
@@ -12,10 +12,17 @@
//
// SPDX-License-Identifier: GPL-2.0-only
/// BEGIN CMAKE
/// target_link_libraries(${SO} PRIVATE "vendored_md5")
/// END CMAKE
#include "module.h"
#include "modules/encryption.h"
#include "md5/md5.c"
extern "C"
{
#include "md5/md5.h"
}
class MD5Context final
: public Encryption::Context
+5 -1
View File
@@ -12,10 +12,14 @@
//
// SPDX-License-Identifier: GPL-2.0-only
/// BEGIN CMAKE
/// target_link_libraries(${SO} PRIVATE "vendored_sha1")
/// END CMAKE
#include "module.h"
#include "modules/encryption.h"
#include "sha1/sha1.c"
#include "sha1/sha1.h"
class SHA1Context final
: public Encryption::Context
+5 -1
View File
@@ -12,10 +12,14 @@
//
// SPDX-License-Identifier: GPL-2.0-only
/// BEGIN CMAKE
/// target_link_libraries(${SO} PRIVATE "vendored_sha2")
/// END CMAKE
#include <climits>
#include <random>
#include "sha2/sha2.c"
#include "sha2/sha2.h"
#include "module.h"
#include "modules/encryption.h"
+2 -4
View File
@@ -634,7 +634,7 @@ public:
{
const Anope::string &cname = it->first;
LDAPService *s = it->second;
int i;
size_t i;
++it;
@@ -654,10 +654,8 @@ public:
}
}
for (int i = 0; i < conf.CountBlock("ldap"); ++i)
for (const auto &[_, ldap] : conf.GetBlocks("ldap"))
{
const auto &ldap = conf.GetBlock("ldap", i);
const Anope::string &connname = ldap.Get<const Anope::string>("name", "ldap/main");
if (this->LDAPServices.find(connname) == this->LDAPServices.end())
+2 -3
View File
@@ -281,7 +281,7 @@ public:
{
const Anope::string &cname = it->first;
MySQLService *s = it->second;
int i;
size_t i;
++it;
@@ -298,9 +298,8 @@ public:
}
}
for (int i = 0; i < config.CountBlock("mysql"); ++i)
for (const auto &[_, block] : config.GetBlocks("mysql"))
{
const auto &block = config.GetBlock("mysql", i);
const Anope::string &connname = block.Get<const Anope::string>("name", "mysql/main");
if (this->MySQLServices.find(connname) == this->MySQLServices.end())
+2 -3
View File
@@ -129,7 +129,7 @@ public:
{
const Anope::string &cname = it->first;
SQLiteService *s = it->second;
int i, num;
size_t i, num;
++it;
for (i = 0, num = config.CountBlock("sqlite"); i < num; ++i)
@@ -145,9 +145,8 @@ public:
}
}
for (int i = 0; i < config.CountBlock("sqlite"); ++i)
for (const auto &[_, block] : config.GetBlocks("sqlite"))
{
const auto &block = config.GetBlock("sqlite", i);
Anope::string connname = block.Get<const Anope::string>("name", "sqlite/main");
if (this->SQLiteServices.find(connname) == this->SQLiteServices.end())
+1 -3
View File
@@ -331,10 +331,8 @@ public:
throw ConfigException("Unable to find http reference, is httpd loaded?");
xmlrpcinterface.tokens.clear();
for (int i = 0; i < modconf.CountBlock("token"); ++i)
for (const auto &[_, block] : modconf.GetBlocks("token"))
{
const auto &block = modconf.GetBlock("token", i);
RPC::Token token;
token.token = block.Get<const Anope::string>("token");
if (!token.token.empty())
+84 -19
View File
@@ -27,6 +27,29 @@ namespace
Anope::string validation_record;
}
struct SharedData final
{
// How long after a requested vhost is activated does a user have to wait before they can request a new vhost.
time_t activationcooldown = 0;
// How long after a requested vhost is rejected does a user have to wait before they can request a new vhost.
time_t rejectioncooldown = 0;
// How long should users have to wait between attempts at DNS validation.
time_t validationcooldown = 0;
// Extensible that stores the time a user had a vhost activated/rejected.
SerializableExtensibleItem<time_t> requestcooldown;
// The name of the DNS record used for validation.
Anope::string validationrecord;
SharedData(Module *mod)
: requestcooldown(mod, "HS_REQUEST_COOLDOWN")
{
}
};
struct HostRequestImpl final
: HostServ::HostRequest
, Serializable
@@ -105,6 +128,7 @@ private:
Command *command;
Reference<NickAlias> nickalias;
CommandSource source;
SharedData &data;
void HandleError(HostRequestImpl *hr)
{
@@ -119,11 +143,12 @@ private:
}
public:
DNSHostResolver(Command *cmd, HostServ::HostRequest *hr, NickAlias *na, const CommandSource &src)
DNSHostResolver(Command *cmd, HostServ::HostRequest *hr, NickAlias *na, const CommandSource &src, SharedData &sd)
: Request(dnsmanager, cmd->module, hr->host, DNS::QUERY_TXT, false)
, command(cmd)
, nickalias(na)
, source(src)
, data(sd)
{
hr->last_validation = Anope::CurTime;
Log(LOG_DEBUG) << "Checking " << hr->host << " for " << hr->validation_token;
@@ -171,6 +196,8 @@ public:
source.Reply(_("VHost for %s has been validated using DNS."), na->nick.c_str());
Log(LOG_COMMAND, source, command) << "for " << na->nick << " for vhost " << hr->Mask();
data.requestcooldown.Set(na, Anope::CurTime + data.activationcooldown);
na->Shrink<HostRequestImpl>(HOSTSERV_HOST_REQUEST_EXT);
return; // We're done.
@@ -183,8 +210,13 @@ public:
class CommandHSRequest final
: public Command
{
private:
SharedData &data;
public:
CommandHSRequest(Module *creator) : Command(creator, "hostserv/request", 1, 1)
CommandHSRequest(Module *creator, SharedData &sd)
: Command(creator, "hostserv/request", 1, 1)
, data(sd)
{
this->SetDesc(_("Request a vhost for your nick"));
this->SetSyntax(_("vhost"));
@@ -263,12 +295,25 @@ public:
return;
}
time_t send_delay = Config->GetModule("memoserv").Get<time_t>("senddelay");
if (Config->GetModule(this->owner).Get<bool>("memooper") && send_delay > 0 && u && u->lastmemosend + send_delay > Anope::CurTime)
time_t waituntil = 0;
{
auto waitperiod = (u->lastmemosend + send_delay) - Anope::CurTime;
// Check whether the user is on a request cooldown.
const auto *last_req = data.requestcooldown.Get(na);
if (last_req)
waituntil = *last_req;
}
if (Config->GetModule(this->owner).Get<bool>("memooper"))
{
// Check whether the user can send a memo to opers yet.
const auto send_delay = Config->GetModule("memoserv").Get<time_t>("senddelay");
if (send_delay > 0 && u && u->lastmemosend)
waituntil = std::max(waituntil, u->lastmemosend + send_delay);
}
if (waituntil && waituntil > Anope::CurTime)
{
const auto waitperiod = waituntil - Anope::CurTime;
source.Reply(_("Please wait %s before requesting a new vhost."), Anope::Duration(waitperiod, source.GetAccount()).c_str());
u->lastmemosend = Anope::CurTime;
return;
}
@@ -319,8 +364,13 @@ public:
class CommandHSActivate final
: public Command
{
private:
SharedData &data;
public:
CommandHSActivate(Module *creator) : Command(creator, "hostserv/activate", 1, 1)
CommandHSActivate(Module *creator, SharedData &sd)
: Command(creator, "hostserv/activate", 1, 1)
, data(sd)
{
this->SetDesc(_("Approve the requested vhost of a user"));
this->SetSyntax(_("\037nick\037"));
@@ -348,6 +398,8 @@ public:
source.Reply(_("VHost for %s has been activated."), na->nick.c_str());
Log(LOG_COMMAND, source, this) << "for " << na->nick << " for vhost " << (!req->ident.empty() ? req->ident + "@" : "") << req->host;
data.requestcooldown.Set(na, Anope::CurTime + data.activationcooldown);
na->Shrink<HostRequestImpl>(HOSTSERV_HOST_REQUEST_EXT);
}
else
@@ -369,8 +421,13 @@ public:
class CommandHSReject final
: public Command
{
private:
SharedData &data;
public:
CommandHSReject(Module *creator) : Command(creator, "hostserv/reject", 1, 2)
CommandHSReject(Module *creator, SharedData &sd)
: Command(creator, "hostserv/reject", 1, 2)
, data(sd)
{
this->SetDesc(_("Reject the requested vhost of a user"));
this->SetSyntax(_("\037nick\037 [\037reason\037]"));
@@ -391,6 +448,7 @@ public:
auto *req = HostRequestImpl::Get(na);
if (req)
{
data.requestcooldown.Set(na, Anope::CurTime + data.rejectioncooldown);
na->Shrink<HostRequestImpl>(HOSTSERV_HOST_REQUEST_EXT);
if (Config->GetModule(this->owner).Get<bool>("memouser") && MemoServ::service)
@@ -481,11 +539,13 @@ public:
class CommandHSValidate final
: public Command
{
public:
time_t cooldown;
private:
SharedData &data;
CommandHSValidate(Module *creator)
public:
CommandHSValidate(Module *creator, SharedData &sd)
: Command(creator, "hostserv/validate", 0)
, data(sd)
{
this->SetDesc(_("Validates a previously requested vhost using DNS"));
}
@@ -512,7 +572,7 @@ public:
return;
}
auto next_validation = req->last_validation + cooldown;
auto next_validation = req->last_validation + data.validationcooldown;
if (req->last_validation && next_validation > Anope::CurTime)
{
source.Reply(_("You must wait for %s before trying DNS validation again."),
@@ -526,7 +586,7 @@ public:
if (!dnsmanager)
throw SocketException("DNS is not available");
res = new DNSHostResolver(this, req, na, source);
res = new DNSHostResolver(this, req, na, source, data);
dnsmanager->Process(res);
}
catch (const SocketException &ex)
@@ -553,8 +613,10 @@ public:
class HSRequest final
: public Module
{
private:
SharedData data;
CommandHSRequest commandhsrequest;
CommandHSActivate commandhsactive;
CommandHSActivate commandhsactivate;
CommandHSReject commandhsreject;
CommandHSWaiting commandhswaiting;
CommandHSValidate commandhsvalidate;
@@ -564,11 +626,12 @@ class HSRequest final
public:
HSRequest(const Anope::string &modname, const Anope::string &creator)
: Module(modname, creator, VENDOR)
, commandhsrequest(this)
, commandhsactive(this)
, commandhsreject(this)
, data(this)
, commandhsrequest(this, data)
, commandhsactivate(this, data)
, commandhsreject(this, data)
, commandhswaiting(this)
, commandhsvalidate(this)
, commandhsvalidate(this, data)
, hostrequest(this, HOSTSERV_HOST_REQUEST_EXT)
{
if (!IRCD || !IRCD->CanSetVHost)
@@ -578,7 +641,9 @@ public:
void OnReload(Configuration::Conf &conf) override
{
const auto &block = conf.GetModule(this);
commandhsvalidate.cooldown = block.Get<time_t>("validationcooldown", "5m");
data.activationcooldown = block.Get<time_t>("activationcooldown", "24h");
data.rejectioncooldown = block.Get<time_t>("rejectioncooldown", "24h");
data.validationcooldown = block.Get<time_t>("validationcooldown", "5m");
validation_record = block.Get<const Anope::string>("validationrecord", "anope-dns-validation");
}
};
+1 -4
View File
@@ -372,11 +372,8 @@ public:
const auto &conf = config.GetModule(this);
std::set<Anope::string> existing;
for (int i = 0; i < conf.CountBlock("httpd"); ++i)
for (const auto &[_, block] : conf.GetBlocks("httpd"))
{
const auto &block = conf.GetBlock("httpd", i);
const Anope::string &hname = block.Get<const Anope::string>("name", "httpd/main");
existing.insert(hname);
+7 -32
View File
@@ -13,34 +13,15 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "module.h"
#include "modules/nickserv/ajoin.h"
struct AJoinEntry;
struct AJoinList final
: Serialize::Checker<std::vector<AJoinEntry *> >
struct AJoinListImpl final : public AJoinList
{
AJoinList(Extensible *) : Serialize::Checker<std::vector<AJoinEntry *> >("AJoinEntry") { }
~AJoinList();
};
struct AJoinEntry final
: Serializable
{
Serialize::Reference<NickCore> owner;
Anope::string channel;
Anope::string key;
AJoinEntry(Extensible *) : Serializable("AJoinEntry") { }
~AJoinEntry() override
AJoinListImpl(Extensible * e) : AJoinList(e) { }
~AJoinListImpl()
{
auto *channels = owner->GetExt<AJoinList>("ajoinlist");
if (channels)
{
auto it = std::find((*channels)->begin(), (*channels)->end(), this);
if (it != (*channels)->end())
(*channels)->erase(it);
}
for (const auto *ajoin : *(*this))
delete ajoin;
}
};
@@ -93,12 +74,6 @@ struct AJoinEntryType final
}
};
AJoinList::~AJoinList()
{
for (const auto *ajoin : *(*this))
delete ajoin;
}
class CommandNSAJoin final
: public Command
{
@@ -321,7 +296,7 @@ class NSAJoin final
: public Module
{
CommandNSAJoin commandnsajoin;
ExtensibleItem<AJoinList> ajoinlist;
ExtensibleItem<AJoinListImpl> ajoinlist;
AJoinEntryType ajoinentry_type;
public:
+5 -3
View File
@@ -330,9 +330,9 @@ public:
command_data.clear();
items_by_priority.clear();
for (int i = 0; i < conf.CountBlock("command"); ++i)
time_t default_priority = 0;
for (const auto &[_, block] : conf.GetBlocks("command"))
{
const auto &block = conf.GetBlock("command", i);
const Anope::string &cmd = block.Get<const Anope::string>("command");
if (cmd != "nickserv/set/misc" && cmd != "nickserv/saset/misc")
continue;
@@ -353,6 +353,8 @@ public:
continue;
}
default_priority += 1000;
data.set_description = desc;
data.pattern = block.Get<const Anope::string>("misc_pattern");
data.syntax = block.Get<const Anope::string>("misc_syntax");
@@ -361,7 +363,7 @@ public:
if (data.priority <= 0)
{
// If no priority is specified, go by order processed
data.priority = i * 1000;
data.priority = default_priority;
}
data.swhois = block.Get<bool>("misc_swhois");
items_by_priority.emplace_back(data.priority, item);
+3 -4
View File
@@ -61,7 +61,7 @@ public:
for (unsigned i = 0; !show_blocks[i].empty(); ++i)
{
const auto &block = Config->GetBlock(show_blocks[i]);
const Configuration::Block::item_map &items = block.GetItems();
const auto &items = block.GetItems();
ListFormatter lflist(source.GetAccount());
lflist.AddColumn(_("Name")).AddColumn(_("Value"));
@@ -84,10 +84,9 @@ public:
lflist.AddColumn(_("Module Name")).AddColumn(_("Name")).AddColumn(_("Value"));
lflist.SetFlexible(_("\002{}{module_name}}:{name}\002 = {value}"));
for (int i = 0; i < Config->CountBlock("module"); ++i)
for (const auto &[_, block] : Config->GetBlocks("module"))
{
const auto &block = Config->GetBlock("module", i);
const Configuration::Block::item_map &items = block.GetItems();
const auto &items = block.GetItems();
if (items.size() <= 1)
continue;
+1 -3
View File
@@ -529,10 +529,8 @@ public:
fileforbids.clear();
const auto &modconf = conf.GetModule(this);
for (auto i = 0; i < modconf.CountBlock("file"); ++i)
for (const auto &[_, fileblock] : modconf.GetBlocks("file"))
{
const auto &fileblock = modconf.GetBlock("file", i);
const auto reasonstr = fileblock.Get<const Anope::string>("reason");
const auto typestr = fileblock.Get<const Anope::string>("type");
+2 -1
View File
@@ -39,7 +39,8 @@ public:
{
bool all = params.size() > 2 && params[2].equals_ci("ALL");
for (const auto &[mode, data] : c->GetModes())
auto cmodes = c->GetModes();
for (const auto &[mode, data] : cmodes)
c->RemoveMode(c->WhoSends(), mode, data.value, false);
if (!c)
+1 -1
View File
@@ -734,7 +734,7 @@ public:
bool IsTagValid(const Anope::string &tname, const Anope::string &tvalue) override
{
// InspIRCd accepts arbitrary message tags.
return true;
return Me->IsSynced();
}
};
+2 -2
View File
@@ -309,9 +309,9 @@ public:
}
this->proxyscans.clear();
for (int i = 0; i < config.CountBlock("proxyscan"); ++i)
for (const auto &[_, block] : config.GetBlocks("proxyscan"))
{
const auto &block = config.GetBlock("proxyscan", i);
ProxyCheck p;
Anope::string token;
+1 -3
View File
@@ -173,10 +173,8 @@ public:
{
Rewrite::rewrites.clear();
for (int i = 0; i < conf.CountBlock("command"); ++i)
for (const auto &[_, block] : conf.GetBlocks("command"))
{
const auto &block = conf.GetBlock("command", i);
if (!block.Get<bool>("rewrite"))
continue;
+9 -4
View File
@@ -12,11 +12,17 @@
//
// SPDX-License-Identifier: GPL-2.0-only
/// BEGIN CMAKE
/// target_link_libraries(${SO} PRIVATE "vendored_yyjson")
/// END CMAKE
#include <cmath>
#include "module.h"
#include "modules/rpc.h"
#include "modules/httpd.h"
#include "yyjson/yyjson.c"
#include "yyjson/yyjson.h"
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
@@ -313,10 +319,9 @@ public:
throw ConfigException("Unable to find http reference, is httpd loaded?");
jsonrpcinterface.tokens.clear();
for (int i = 0; i < modconf.CountBlock("token"); ++i)
{
const auto &block = modconf.GetBlock("token", i);
for (const auto &[_, block] : modconf.GetBlocks("token"))
{
RPC::Token token;
token.token = block.Get<const Anope::string>("token");
if (!token.token.empty())
+1 -1
View File
@@ -25,7 +25,7 @@ if(HAVE_LOCALIZATION)
)
# Install the new language file
install(CODE "FILE(MAKE_DIRECTORY ${LOCALE_DIR}/${LANG_LANG}/LC_MESSAGES/)")
install(DIRECTORY DESTINATION "${LOCALE_DIR}/${LANG_LANG}/LC_MESSAGES")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG_MO} DESTINATION ${LOCALE_DIR}/${LANG_LANG}/LC_MESSAGES RENAME ${LANG_DOMAIN}.mo PERMISSIONS ${PERMS})
endforeach()
+1 -1
View File
@@ -402,5 +402,5 @@ bool Command::FindFromService(const Anope::string &command_service, BotInfo *&bo
}
}
return name.empty();
return !name.empty();
}
+34 -47
View File
@@ -38,28 +38,37 @@ const Anope::string &Configuration::Block::GetName() const
return name;
}
int Configuration::Block::CountBlock(const Anope::string &bname) const
size_t Configuration::Block::CountBlock(const Anope::string &bname) const
{
return blocks.count(bname);
}
const Configuration::Block &Configuration::Block::GetBlock(const Anope::string &bname, int num) const
Configuration::Block::BlockList Configuration::Block::GetBlocks(const Anope::string &bname) const
{
std::pair<block_map::const_iterator, block_map::const_iterator> it = blocks.equal_range(bname);
return Anope::equal_range(blocks, bname);
}
for (int i = 0; it.first != it.second; ++it.first, ++i)
const Configuration::Block &Configuration::Block::GetBlock(const Anope::string &bname, size_t num) const
{
auto it = blocks.equal_range(bname);
for (size_t i = 0; it.first != it.second; ++it.first, ++i)
{
if (i == num)
return it.first->second;
}
return EmptyBlock;
}
Configuration::Block *Configuration::Block::GetMutableBlock(const Anope::string &bname, int num)
Configuration::Block *Configuration::Block::GetMutableBlock(const Anope::string &bname, size_t num)
{
std::pair<block_map::iterator, block_map::iterator> it = blocks.equal_range(bname);
auto it = blocks.equal_range(bname);
for (int i = 0; it.first != it.second; ++it.first, ++i)
for (size_t i = 0; it.first != it.second; ++it.first, ++i)
{
if (i == num)
return &it.first->second;
}
return NULL;
}
@@ -69,7 +78,7 @@ bool Configuration::Block::Set(const Anope::string &tag, const Anope::string &va
return true;
}
const Configuration::Block::item_map &Configuration::Block::GetItems() const
const Configuration::Block::ItemMap &Configuration::Block::GetItems() const
{
return items;
}
@@ -125,10 +134,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
this->LoadConf(ServicesConf);
for (int i = 0; i < this->CountBlock("include"); ++i)
for (const auto &[_, include] : this->GetBlocks("include"))
{
const auto &include = this->GetBlock("include", i);
const Anope::string &type = include.Get<const Anope::string>("type"),
&file = include.Get<const Anope::string>("name");
@@ -205,10 +212,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
this->TimeoutCheck = options.Get<time_t>("timeoutcheck");
this->NickChars = networkinfo.Get<Anope::string>("nick_chars");
for (int i = 0; i < this->CountBlock("uplink"); ++i)
for (const auto &[_, uplink] : this->GetBlocks("uplink"))
{
const auto &uplink = this->GetBlock("uplink", i);
int protocol;
const Anope::string &protocolstr = uplink.Get<const Anope::string>("protocol", "ipv4");
if (protocolstr == "ipv4")
@@ -238,10 +243,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
this->Uplinks.emplace_back(host, port, password, protocol);
}
for (int i = 0; i < this->CountBlock("module"); ++i)
for (const auto &[_, module] : this->GetBlocks("module"))
{
const auto &module = this->GetBlock("module", i);
const Anope::string &modname = module.Get<const Anope::string>("name");
ValidateNotEmptyOrSpaces("module", "name", modname);
@@ -249,10 +252,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
this->ModulesAutoLoad.push_back(modname);
}
for (int i = 0; i < this->CountBlock("opertype"); ++i)
for (const auto &[_, opertype] : this->GetBlocks("opertype"))
{
const auto &opertype = this->GetBlock("opertype", i);
const Anope::string &oname = opertype.Get<const Anope::string>("name"),
&modes = opertype.Get<const Anope::string>("modes"),
&inherits = opertype.Get<const Anope::string>("inherits"),
@@ -292,10 +293,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
this->MyOperTypes.push_back(ot);
}
for (int i = 0; i < this->CountBlock("oper"); ++i)
for (const auto &[_, oper] : this->GetBlocks("oper"))
{
const auto &oper = this->GetBlock("oper", i);
const Anope::string &nname = oper.Get<const Anope::string>("name"),
&type = oper.Get<const Anope::string>("type"),
&password = oper.Get<const Anope::string>("password"),
@@ -330,10 +329,9 @@ Configuration::Conf::Conf() : Configuration::Block("")
for (const auto &[_, bi] : *BotListByNick)
bi->conf = false;
for (int i = 0; i < this->CountBlock("service"); ++i)
{
const auto &service = this->GetBlock("service", i);
for (const auto &[_, service] : this->GetBlocks("service"))
{
const Anope::string &nick = service.Get<const Anope::string>("nick"),
&user = service.Get<const Anope::string>("user", nick.lower()),
&host = service.Get<const Anope::string>("host", servername),
@@ -421,10 +419,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
}
}
for (int i = 0; i < this->CountBlock("log"); ++i)
for (const auto &[_, log] : this->GetBlocks("log"))
{
const auto &log = this->GetBlock("log", i);
int logage = log.Get<int>("logage");
bool rawio = log.Get<bool>("rawio");
bool debug = log.Get<bool>("debug");
@@ -447,10 +443,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
for (const auto &[_, bi] : *BotListByNick)
bi->commands.clear();
for (int i = 0; i < this->CountBlock("command"); ++i)
for (const auto &[_, command] : this->GetBlocks("command"))
{
const auto &command = this->GetBlock("command", i);
const Anope::string &service = command.Get<const Anope::string>("service"),
&nname = command.Get<const Anope::string>("name"),
&cmd = command.Get<const Anope::string>("command"),
@@ -472,10 +466,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
}
PrivilegeManager::ClearPrivileges();
for (int i = 0; i < this->CountBlock("privilege"); ++i)
for (const auto &[_, privilege] : this->GetBlocks("privilege"))
{
const auto &privilege = this->GetBlock("privilege", i);
const Anope::string &nname = privilege.Get<const Anope::string>("name"),
&desc = privilege.Get<const Anope::string>("desc");
int rank = privilege.Get<int>("rank");
@@ -483,10 +475,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
PrivilegeManager::AddPrivilege(Privilege(nname, desc, rank));
}
for (int i = 0; i < this->CountBlock("fantasy"); ++i)
for (const auto &[_, fantasy] : this->GetBlocks("fantasy"))
{
const auto &fantasy = this->GetBlock("fantasy", i);
const Anope::string &nname = fantasy.Get<const Anope::string>("name"),
&service = fantasy.Get<const Anope::string>("command"),
&permission = fantasy.Get<const Anope::string>("permission"),
@@ -504,10 +494,8 @@ Configuration::Conf::Conf() : Configuration::Block("")
c.require_privilege = fantasy.Get<bool>("require_privilege", "yes");
}
for (int i = 0; i < this->CountBlock("command_group"); ++i)
for (const auto &[_, command_group] : this->GetBlocks("command_group"))
{
const auto &command_group = this->GetBlock("command_group", i);
const Anope::string &nname = command_group.Get<const Anope::string>("name"),
&description = command_group.Get<const Anope::string>("description");
@@ -655,7 +643,7 @@ Configuration::Block &Configuration::Conf::GetModule(const Anope::string &mname)
auto *&block = modules[mname];
/* Search for the block */
for (std::pair<block_map::iterator, block_map::iterator> iters = blocks.equal_range("module"); iters.first != iters.second; ++iters.first)
for (auto iters = blocks.equal_range("module"); iters.first != iters.second; ++iters.first)
{
auto &b = iters.first->second;
@@ -688,7 +676,7 @@ const Configuration::Block &Configuration::Conf::GetCommand(CommandSource &sourc
{
const Anope::string &block_name = source.c ? "fantasy" : "command";
for (std::pair<block_map::iterator, block_map::iterator> iters = blocks.equal_range(block_name); iters.first != iters.second; ++iters.first)
for (auto iters = blocks.equal_range(block_name); iters.first != iters.second; ++iters.first)
{
auto &b = iters.first->second;
@@ -782,7 +770,7 @@ void Configuration::Conf::LoadConf(Configuration::File &file)
Anope::string itemname, wordbuffer;
std::stack<Configuration::Block *> block_stack;
int linenumber = 0;
unsigned linenumber = 0;
bool in_word = false, in_quote = false, in_comment = false;
Log(LOG_DEBUG) << "Start to read conf " << file.GetPath();
@@ -984,7 +972,7 @@ void Configuration::Conf::LoadConf(Configuration::File &file)
}
}
Anope::string Configuration::Conf::ReplaceVars(const Anope::string &str, const Configuration::File &file, int linenumber)
Anope::string Configuration::Conf::ReplaceVars(const Anope::string &str, const Configuration::File &file, unsigned linenumber)
{
Anope::string ret;
for (auto it = str.begin(); it != str.end(); )
@@ -1020,9 +1008,8 @@ Anope::string Configuration::Conf::ReplaceVars(const Anope::string &str, const C
}
auto found = false;
for (int i = 0; i < this->CountBlock("define"); ++i)
for (const auto &[_, define] : this->GetBlocks("define"))
{
const auto &define = this->GetBlock("define", i);
const auto defname = define.Get<const Anope::string>("name");
if (defname == var)
{
+2 -2
View File
@@ -537,8 +537,8 @@ bool Anope::Init(int ac, char **av)
/* load modules */
Log() << "Loading modules...";
for (int i = 0; i < Config->CountBlock("module"); ++i)
ModuleManager::LoadModule(Config->GetBlock("module", i).Get<const Anope::string>("name"), NULL);
for (const auto &[_, block] : Config->GetBlocks("module"))
ModuleManager::LoadModule(block.Get<const Anope::string>("name"), NULL);
#ifndef _WIN32
/* If we're root, issue a warning now */
+20 -2
View File
@@ -952,9 +952,9 @@ Anope::string Anope::VersionShort()
Anope::string Anope::VersionBuildString()
{
#if REPRODUCIBLE_BUILD
Anope::string s = "build #" + Anope::ToString(BUILD);
auto s = Anope::Format("build #%u", BUILD);
#else
Anope::string s = "build #" + Anope::ToString(BUILD) + ", compiled " + Anope::compiled;
auto s = Anope::Format("build #%u, compiled %s", BUILD, BUILD_DATE);
#endif
Anope::string flags;
@@ -1178,6 +1178,24 @@ Anope::string Anope::FormatCTCP(const Anope::string &name, const Anope::string &
return Anope::Format("\1%s %s\1", name.c_str(), value.c_str());
}
Anope::string Anope::FormatISO8601(time_t ts, unsigned long long ms)
{
static time_t last_ts = -1;
static unsigned long long last_ms = -1;
static Anope::string timestamp;
if (ts != last_ts || ms != last_ms)
{
last_ts = ts;
last_ms = ms;
char timebuf[32];
const auto *tm = gmtime(&ts);
strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", tm);
timestamp = Anope::Format("%s.%03lldZ", timebuf, ms);
}
return timestamp;
}
bool Anope::ParseCTCP(const Anope::string &text, Anope::string &name, Anope::string &body)
{
// According to draft-oakley-irc-ctcp-02 a valid CTCP must begin with SOH and
+7
View File
@@ -100,6 +100,13 @@ void Anope::ProcessInternal(MessageSource &src, const Anope::string &command, co
}
}
void IRCDProto::PopulateTags(Anope::map<Anope::string> &tags, const MessageSource &source, const Anope::string &command, const std::vector<Anope::string> &params)
{
auto it = tags.find("time"); // https://ircv3.net/specs/extensions/server-time
if (it == tags.end())
tags["time"] = Anope::FormatISO8601(Anope::CurTime, Anope::CurTimeNs / 1'000'000);
}
bool IRCDProto::Parse(const Anope::string &buffer, Anope::map<Anope::string> &tags, Anope::string &source, Anope::string &command, std::vector<Anope::string> &params)
{
MessageTokenizer tokens(buffer);
+12 -5
View File
@@ -73,8 +73,11 @@ void Uplink::SendInternal(const Anope::map<Anope::string> &tags, const MessageSo
return;
}
Anope::map<Anope::string> fulltags(tags);
IRCD->PopulateTags(fulltags, source, command, params);
Anope::string message;
if (!IRCD->Format(message, tags, source, command, params))
if (!IRCD->Format(message, fulltags, source, command, params))
return;
UplinkSock->sent_msgs++;
@@ -83,13 +86,17 @@ void Uplink::SendInternal(const Anope::map<Anope::string> &tags, const MessageSo
Log(LOG_RAWIO) << "Sent " << message;
if (Anope::ProtocolDebug)
{
if (tags.empty())
Log() << "\tNo tags";
else
auto sent_tag = false;
for (const auto &[tname, tvalue] : fulltags)
{
for (const auto &[tname, tvalue] : tags)
if (IRCD->IsTagValid(tname, tvalue))
{
Log() << "\tTag " << tname << ": " << tvalue;
sent_tag = true;
}
}
if (!sent_tag)
Log() << "\tNo tags";
if (source.GetSource().empty())
Log() << "\tNo source";
+21
View File
@@ -0,0 +1,21 @@
file(GLOB LIBRARIES CONFIGURE_DEPENDS "*")
foreach(LIBRARY IN LISTS LIBRARIES)
if(IS_DIRECTORY ${LIBRARY})
file(GLOB_RECURSE LIBRARY_SOURCES CONFIGURE_DEPENDS
"${LIBRARY}/*.c"
"${LIBRARY}/*.cc"
"${LIBRARY}/*.cpp"
)
if(LIBRARY_SOURCES)
cmake_path(GET LIBRARY FILENAME LIBRARY_NAME)
set(LIBRARY_TARGET "vendored_${LIBRARY_NAME}")
add_library(${LIBRARY_TARGET} STATIC ${LIBRARY_SOURCES})
target_compile_options(${LIBRARY_TARGET} PRIVATE "$<IF:$<BOOL:${MSVC}>,/W0,-w>")
set_target_properties(${LIBRARY_TARGET} PROPERTIES
FOLDER "VendoredLibraries"
PREFIX ""
UNITY_BUILD OFF
)
endif()
endif()
endforeach()