mirror of
https://github.com/anope/anope.git
synced 2026-07-05 03:13:13 +02:00
Compare commits
22 Commits
d1f6da1817
...
2.1
| Author | SHA1 | Date | |
|---|---|---|---|
| c784f08002 | |||
| 6de2ae0046 | |||
| c26f742662 | |||
| e3bff85339 | |||
| 5819c1fcf3 | |||
| 4b8f073cb7 | |||
| 67026b184d | |||
| b49f984598 | |||
| b9cacf1d0f | |||
| 09d0bd6987 | |||
| f150ee857f | |||
| d60c80a4a3 | |||
| 44b7493eb1 | |||
| a03e765172 | |||
| 17cd10ef21 | |||
| ca8fcbe119 | |||
| b9d0762f2b | |||
| 4878b1787e | |||
| 0163d92b1d | |||
| a861a059f6 | |||
| 90da25f84f | |||
| ab362c9828 |
@@ -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: |-
|
||||
|
||||
@@ -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: |-
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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); }
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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> ¶ms);
|
||||
|
||||
/* 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> ¶ms);
|
||||
|
||||
/* 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.
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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
@@ -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
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
@@ -402,5 +402,5 @@ bool Command::FindFromService(const Anope::string &command_service, BotInfo *&bo
|
||||
}
|
||||
}
|
||||
|
||||
return name.empty();
|
||||
return !name.empty();
|
||||
}
|
||||
|
||||
+34
-47
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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> ¶ms)
|
||||
{
|
||||
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> ¶ms)
|
||||
{
|
||||
MessageTokenizer tokens(buffer);
|
||||
|
||||
+12
-5
@@ -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";
|
||||
|
||||
Vendored
+21
@@ -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()
|
||||
Reference in New Issue
Block a user