mirror of
https://github.com/anope/anope.git
synced 2026-07-05 05:33:13 +02:00
Compare commits
122 Commits
| 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 | |||
| d1f6da1817 | |||
| 9280d90aba | |||
| c71f69208d | |||
| d0080b85a7 | |||
| 745e94783d | |||
| c32be6a711 | |||
| 9dd3acfc87 | |||
| 82b6da3763 | |||
| 8e691eac80 | |||
| 2327c6ac9a | |||
| e23ea8f8ea | |||
| 449cfa6503 | |||
| 59c60849b6 | |||
| 6d2c4fb612 | |||
| 01fc7421b6 | |||
| 3a59a81aa4 | |||
| c24de0ca8f | |||
| 18555638c9 | |||
| d5a653fe94 | |||
| ad3be96283 | |||
| d689c0686d | |||
| 4ca2952069 | |||
| caf904c938 | |||
| 8930cc2a92 | |||
| 47b927d788 | |||
| 647a07ff96 | |||
| a856e80b1e | |||
| 55746e1098 | |||
| e0da3bae18 | |||
| c1cf7b0bcc | |||
| 1cf64a49c0 | |||
| 92ed0071d7 | |||
| 9469cb8b0e | |||
| e0b715b185 | |||
| e6d7306202 | |||
| 2107d18d9e | |||
| acb9428635 | |||
| d942da1df5 | |||
| a961259556 | |||
| 27a19dcc52 | |||
| e45814bfd6 | |||
| f12b590a6d | |||
| c0bafe10b4 | |||
| 5f40d0d6bb | |||
| 6b416cc64d | |||
| 331c33c350 | |||
| 10eef1af23 | |||
| 90ff716ed3 | |||
| f67c70e485 | |||
| fd5e10c54c | |||
| 029565c894 | |||
| 9b8862826c | |||
| e2dc77641a | |||
| 7eb710a009 | |||
| b33b5a6630 | |||
| f4d5b1f01d | |||
| b61daf81b0 | |||
| da3f667188 | |||
| 040cd99027 | |||
| 6c7977f239 | |||
| 9b8570a2ee | |||
| 64f386e29e | |||
| 9434be29bc | |||
| 9aff71fb2f | |||
| ba26d9a15c | |||
| 22dc33de9f | |||
| 11d6f58a1a | |||
| f32d6453f5 | |||
| 1606bb01a3 | |||
| 3d86c82ac5 | |||
| a858a1e0a0 | |||
| eaa24d3b0a | |||
| 5355b53008 | |||
| 85a32077b5 | |||
| 5006993d0a | |||
| 1b2b4386c1 | |||
| b199d74088 | |||
| 7e2ef3774b | |||
| e002f39509 | |||
| d3395a5d39 | |||
| 1deff337fe | |||
| adf457ffb2 | |||
| 550adc13f3 | |||
| 04bdcb8ce6 | |||
| 0a99866e5b | |||
| c22f7a9038 | |||
| 074ad6556c | |||
| a657cb5edf | |||
| 63032dc8d9 | |||
| 404f55502e | |||
| 73f3621af6 | |||
| 2a1409face | |||
| 9834040948 | |||
| f111e3b3ec | |||
| 22a1924bfd | |||
| fa5bb3f1bf | |||
| 12545ccbde | |||
| 98703052cc | |||
| 15635779af | |||
| 1fc58c7a57 |
@@ -35,5 +35,6 @@ I have ensured that:
|
|||||||
|
|
||||||
- [ ] The code I am submitting is my own work and/or I have permission from the author to share it.
|
- [ ] The code I am submitting is my own work and/or I have permission from the author to share it.
|
||||||
- [ ] Generative AI (Copilot, ChatGPT, etc) was not used to create any part of this pull request.
|
- [ ] Generative AI (Copilot, ChatGPT, etc) was not used to create any part of this pull request.
|
||||||
- [ ] I have documented any features added by this pull request.
|
- [ ] If the pull request contains a security fix I have followed the reporting rules mentioned in [the security policy](https://github.com/anope/anope/security/policy) (delete if not applicable).
|
||||||
|
- [ ] I have documented any features added by this pull request (delete if not applicable).
|
||||||
- [ ] This pull request does not introduce any incompatible API changes (stable branches only, delete if not applicable).
|
- [ ] This pull request does not introduce any incompatible API changes (stable branches only, delete if not applicable).
|
||||||
|
|||||||
@@ -3,21 +3,18 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 0'
|
- cron: 0 0 * * 0
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: "!contains(github.event.head_commit.message, '[skip alpine ci]')"
|
if: "!contains(github.event.head_commit.message, '[skip alpine ci]')"
|
||||||
container: alpine:edge
|
container: alpine:latest
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
|
||||||
CXX: ${{ matrix.compiler }}
|
|
||||||
CXXFLAGS: -Werror
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |-
|
||||||
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
||||||
apk update
|
apk update
|
||||||
apk add \
|
apk add \
|
||||||
@@ -37,24 +34,36 @@ jobs:
|
|||||||
tre-dev
|
tre-dev
|
||||||
|
|
||||||
- name: Enable extras
|
- name: Enable extras
|
||||||
run: |
|
run: |-
|
||||||
for MODULE in enc_argon2 enc_posix ldap mysql regex_pcre2 regex_posix regex_tre sqlite ssl_gnutls ssl_openssl
|
for MODULE in enc_argon2 enc_posix ldap mysql regex_pcre2 regex_posix regex_tre sqlite ssl_gnutls ssl_openssl
|
||||||
do
|
do
|
||||||
ln -s $PWD/modules/extra/$MODULE.cpp $PWD/modules
|
ln -s $PWD/modules/extra/$MODULE.cpp $PWD/modules
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: |
|
env:
|
||||||
mkdir build && cd build
|
CC: ${{ matrix.compiler.cc }}
|
||||||
cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=DEBUG -DINSTDIR:STRING=$(readlink -f ../run) ..
|
CXX: ${{ matrix.compiler.cxx }}
|
||||||
|
CXXFLAGS: -Werror
|
||||||
|
run: |-
|
||||||
|
cmake -B "build" \
|
||||||
|
-D "CMAKE_BUILD_TYPE=Debug" \
|
||||||
|
-D "INSTDIR=$(readlink -f ../run)" \
|
||||||
|
-G "Ninja" \
|
||||||
|
-Wdeprecated \
|
||||||
|
-Wdev
|
||||||
|
|
||||||
- name: Build Anope
|
- name: Build and install
|
||||||
run: |
|
env:
|
||||||
ninja -C build install
|
VERBOSE: ${{ runner.debug }}
|
||||||
|
run: |-
|
||||||
|
ninja -C "build" ${{ runner.debug == '1' && '-v' || '' }} install
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
compiler:
|
compiler:
|
||||||
- clang++
|
- cc: clang
|
||||||
- g++
|
cxx: clang++
|
||||||
|
- cc: gcc
|
||||||
|
cxx: g++
|
||||||
|
|||||||
@@ -3,20 +3,17 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 0'
|
- cron: 0 0 * * 0
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: "!contains(github.event.head_commit.message, '[skip ubuntu ci]')"
|
if: "!contains(github.event.head_commit.message, '[skip ubuntu ci]')"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
env:
|
|
||||||
CXX: ${{ matrix.compiler }}
|
|
||||||
CXXFLAGS: -Werror
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |-
|
||||||
sudo apt-get update --assume-yes
|
sudo apt-get update --assume-yes
|
||||||
sudo apt-get install --assume-yes --no-install-recommends \
|
sudo apt-get install --assume-yes --no-install-recommends \
|
||||||
clang \
|
clang \
|
||||||
@@ -34,24 +31,36 @@ jobs:
|
|||||||
ninja-build
|
ninja-build
|
||||||
|
|
||||||
- name: Enable extras
|
- name: Enable extras
|
||||||
run: |
|
run: |-
|
||||||
for MODULE in enc_argon2 enc_posix ldap mysql regex_pcre2 regex_posix regex_tre sqlite ssl_gnutls ssl_openssl
|
for MODULE in enc_argon2 enc_posix ldap mysql regex_pcre2 regex_posix regex_tre sqlite ssl_gnutls ssl_openssl
|
||||||
do
|
do
|
||||||
ln -s ${{ github.workspace }}/modules/extra/$MODULE.cpp ${{ github.workspace }}/modules
|
ln -s ${{ github.workspace }}/modules/extra/$MODULE.cpp ${{ github.workspace }}/modules
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: |
|
env:
|
||||||
mkdir build && cd build
|
CC: ${{ matrix.compiler.cc }}
|
||||||
cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=DEBUG -DINSTDIR:STRING=${{ github.workspace }}/run ..
|
CXX: ${{ matrix.compiler.cxx }}
|
||||||
|
CXXFLAGS: -Werror
|
||||||
|
run: |-
|
||||||
|
cmake -B "build" \
|
||||||
|
-D "CMAKE_BUILD_TYPE=Debug" \
|
||||||
|
-D "INSTDIR=${{ github.workspace }}/run" \
|
||||||
|
-G "Ninja" \
|
||||||
|
-Wdeprecated \
|
||||||
|
-Wdev
|
||||||
|
|
||||||
- name: Build Anope
|
- name: Build and install
|
||||||
run: |
|
env:
|
||||||
ninja -C ${{ github.workspace }}/build install
|
VERBOSE: ${{ runner.debug }}
|
||||||
|
run: |-
|
||||||
|
ninja -C "build" ${{ runner.debug == '1' && '-v' || '' }} install
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
compiler:
|
compiler:
|
||||||
- clang++
|
- cc: clang
|
||||||
- g++
|
cxx: clang++
|
||||||
|
- cc: gcc
|
||||||
|
cxx: g++
|
||||||
|
|||||||
@@ -6,78 +6,91 @@ on:
|
|||||||
types:
|
types:
|
||||||
- published
|
- published
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 0'
|
- cron: 0 0 * * 0
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: "!contains(github.event.head_commit.message, '[skip windows ci]')"
|
if: "!contains(github.event.head_commit.message, '[skip windows ci]')"
|
||||||
runs-on: windows-2025
|
runs-on: windows-2025
|
||||||
env:
|
env:
|
||||||
|
BUILD_PATH: ${{ github.workspace }}\build
|
||||||
BUILD_TYPE: ${{ github.event_name == 'release' && 'Release' || 'Debug' }}
|
BUILD_TYPE: ${{ github.event_name == 'release' && 'Release' || 'Debug' }}
|
||||||
|
CONAN_FILE: ${{ github.workspace }}\src\win32\conanfile.txt
|
||||||
CONAN_HOME: ${{ github.workspace }}\build\conan
|
CONAN_HOME: ${{ github.workspace }}\build\conan
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Install NSIS
|
- name: Setup MSVC
|
||||||
uses: negrutiu/nsis-install@v2
|
uses: TheMrMilchmann/setup-msvc-dev@v4
|
||||||
|
with:
|
||||||
|
arch: x64
|
||||||
|
|
||||||
|
- name: Setup NSIS
|
||||||
|
uses: negrutiu/nsis-install@v3
|
||||||
with:
|
with:
|
||||||
distro: official
|
distro: official
|
||||||
|
|
||||||
- name: Setup MSBuild
|
|
||||||
uses: microsoft/setup-msbuild@v2
|
|
||||||
|
|
||||||
- name: Setup Conan
|
- name: Setup Conan
|
||||||
uses: turtlebrowser/get-conan@v1.2
|
uses: turtlebrowser/get-conan@v1.2
|
||||||
|
|
||||||
- name: Create Conan profile
|
- name: Create Conan configuration
|
||||||
run: |-
|
run: |-
|
||||||
conan profile detect
|
conan profile detect
|
||||||
(Get-Content ${{ env.CONAN_HOME }}\profiles\default).replace('build_type=Release', 'build_type=${{ env.BUILD_TYPE }}') | Set-Content ${{ env.CONAN_HOME }}\profiles\default
|
(Get-Content ${{ env.CONAN_HOME }}\profiles\default).replace('build_type=Release', 'build_type=${{ env.BUILD_TYPE }}') | Set-Content ${{ env.CONAN_HOME }}\profiles\default
|
||||||
(Get-Content ${{ env.CONAN_HOME }}\profiles\default).replace('compiler.cppstd=14', 'compiler.cppstd=17') | Set-Content ${{ env.CONAN_HOME }}\profiles\default
|
(Get-Content ${{ env.CONAN_HOME }}\profiles\default).replace('compiler.cppstd=14', 'compiler.cppstd=17') | Set-Content ${{ env.CONAN_HOME }}\profiles\default
|
||||||
|
|
||||||
Write-Output 'core.sources:download_urls=["origin", "https://c3i.jfrog.io/artifactory/conan-center-backup-sources/"]' | Out-File -Append ${{ env.CONAN_HOME }}\global.conf
|
Write-Output 'core.sources:download_urls=["origin", "https://c3i.jfrog.io/artifactory/conan-center-backup-sources/"]' | Out-File -Append ${{ env.CONAN_HOME }}\global.conf
|
||||||
|
Write-Output 'tools.cmake.cmaketoolchain:generator=Ninja' | Out-File -Append ${{ env.CONAN_HOME }}\global.conf
|
||||||
|
Write-Output 'user.openssl:windows_use_jom=True' | Out-File -Append ${{ env.CONAN_HOME }}\global.conf
|
||||||
|
|
||||||
- name: Try to restore libraries from the cache
|
- name: Try to restore libraries from the cache
|
||||||
uses: actions/cache/restore@v5
|
if: github.event_name != 'release'
|
||||||
|
uses: actions/cache/restore@v6
|
||||||
id: library-cache
|
id: library-cache
|
||||||
with:
|
with:
|
||||||
key: conan-${{ env.BUILD_TYPE }}-${{ hashFiles('src\win32\conanfile.txt') }}
|
key: Conan VS${{ env.VisualStudioVersion }} ${{ env.BUILD_TYPE }} ${{ hashFiles(env.CONAN_FILE) }}
|
||||||
path: ${{ env.CONAN_HOME }}\p
|
path: ${{ env.CONAN_HOME }}/p
|
||||||
|
|
||||||
- name: Install libraries
|
- name: Install libraries
|
||||||
run: |
|
working-directory: ${{ env.BUILD_PATH }}
|
||||||
(Get-Content ${{ github.workspace }}\src\win32\conanfile.txt).replace('##', '') | Set-Content ${{ github.workspace }}\src\win32\conanfile.txt
|
run: |-
|
||||||
conan install ${{ github.workspace }}\src\win32 --build missing --deployer runtime_deploy --deployer-folder ${{ github.workspace }}\build\extradll --output-folder .
|
(Get-Content ${{ env.CONAN_FILE }}).replace('##', '') | Set-Content ${{ env.CONAN_FILE }}
|
||||||
|
conan install ${{ env.CONAN_FILE }} --build missing --deployer runtime_deploy --deployer-folder extradll --output-folder .
|
||||||
|
conan cache clean "*"
|
||||||
|
|
||||||
- name: Save libraries to the cache
|
- name: Save libraries to the cache
|
||||||
if: ${{ steps.library-cache.outputs.cache-hit != 'true' }}
|
if: steps.library-cache.outputs.cache-hit != 'true' && github.event_name != 'release'
|
||||||
uses: actions/cache/save@v5
|
uses: actions/cache/save@v6
|
||||||
with:
|
with:
|
||||||
key: ${{ steps.library-cache.outputs.cache-primary-key }}
|
key: ${{ steps.library-cache.outputs.cache-primary-key }}
|
||||||
path: ${{ env.CONAN_HOME }}\p
|
path: ${{ env.CONAN_HOME }}\p
|
||||||
|
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
shell: cmd # work around a conan-io/conan-center-index bug #21823
|
working-directory: ${{ env.BUILD_PATH }}
|
||||||
working-directory: ${{ github.workspace }}\build
|
run: |-
|
||||||
run: |
|
cmake -D "CMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}" `
|
||||||
call ${{ github.workspace }}\conanbuild.bat
|
-D "CMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake" `
|
||||||
cmake -A x64 -D "CMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}" -D CMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -G "Visual Studio 17 2022" ..
|
-G "Ninja Multi-Config" `
|
||||||
|
-S "${{ github.workspace }}" `
|
||||||
|
-Wdeprecated `
|
||||||
|
-Wdev
|
||||||
|
|
||||||
- name: Build Anope
|
- name: Build installer
|
||||||
working-directory: ${{ github.workspace }}\build
|
working-directory: ${{ env.BUILD_PATH }}
|
||||||
run: |
|
run: |-
|
||||||
msbuild PACKAGE.vcxproj /M:5 /P:Configuration=${{ env.BUILD_TYPE }} /P:Platform=x64 /VERBOSITY:MINIMAL
|
ninja -f "build-${{ env.BUILD_TYPE }}.ninja" ${{ runner.debug == '1' && '-v' || '' }} package
|
||||||
|
|
||||||
- name: Upload installer
|
- name: Upload installer
|
||||||
if: "${{ github.event_name == 'release' }}"
|
if: github.event_name == 'release'
|
||||||
working-directory: ${{ github.workspace }}\build
|
working-directory: ${{ env.BUILD_PATH }}
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
gh release upload ${{ github.event.release.tag_name }} $(Get-ChildItem anope-*.exe)
|
gh release upload ${{ github.event.release.tag_name }} $(Get-ChildItem anope-*.exe)
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
if: "${{ github.event_name != 'release' }}"
|
if: github.event_name != 'release'
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: windows-installer
|
name: windows-installer
|
||||||
path: ${{ github.workspace }}\build\\anope-*.exe
|
path: ${{ github.workspace }}\build\\anope-*.exe
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -51,7 +51,8 @@ Robby <robby@chatbelgie.be> <robby@chat.be>
|
|||||||
Robert Scheck <robert@fedoraproject.org> <robert-scheck@users.noreply.github.com>
|
Robert Scheck <robert@fedoraproject.org> <robert-scheck@users.noreply.github.com>
|
||||||
Robin Burchell <w00t@inspircd.org> <rburchell@5417fbe8-f217-4b02-8779-1006273d7864>
|
Robin Burchell <w00t@inspircd.org> <rburchell@5417fbe8-f217-4b02-8779-1006273d7864>
|
||||||
Robin Burchell <w00t@inspircd.org> <Robin Burchell w00t@inspircd.org@5417fbe8-f217-4b02-8779-1006273d7864>
|
Robin Burchell <w00t@inspircd.org> <Robin Burchell w00t@inspircd.org@5417fbe8-f217-4b02-8779-1006273d7864>
|
||||||
Sadie Powell <sadie@witchery.services> <petpow@saberuk.com>
|
Sadie Powell <sadie@sadiepowell.dev> <petpow@saberuk.com>
|
||||||
|
Sadie Powell <sadie@sadiepowell.dev> <sadie@witchery.services>
|
||||||
Sebastian Barfurth <github@afreshmelon.com>
|
Sebastian Barfurth <github@afreshmelon.com>
|
||||||
Sebastian V. <hal9000@denorastats.org>
|
Sebastian V. <hal9000@denorastats.org>
|
||||||
Sebastian V. <hal9000@denorastats.org> <pimpmylinux@5417fbe8-f217-4b02-8779-1006273d7864>
|
Sebastian V. <hal9000@denorastats.org> <pimpmylinux@5417fbe8-f217-4b02-8779-1006273d7864>
|
||||||
|
|||||||
+42
-31
@@ -1,8 +1,8 @@
|
|||||||
# This usage of CMake requires at least version 3.20
|
# This usage of CMake requires at least version 3.20
|
||||||
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
|
||||||
|
|
||||||
# Set the project as C++ primarily
|
# Set the project as C++ primarily with C for the vendored libraries
|
||||||
project(Anope CXX)
|
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
|
# 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)
|
set(ENV{LC_ALL} C)
|
||||||
@@ -25,6 +25,7 @@ include(CheckFunctionExists)
|
|||||||
include(CheckTypeSize)
|
include(CheckTypeSize)
|
||||||
include(CheckLibraryExists)
|
include(CheckLibraryExists)
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
|
include(CheckPIESupported)
|
||||||
|
|
||||||
if(NOT WIN32)
|
if(NOT WIN32)
|
||||||
include(FindPkgConfig)
|
include(FindPkgConfig)
|
||||||
@@ -40,15 +41,19 @@ if(EXTRA_LIBS)
|
|||||||
link_directories(${EXTRA_LIBS})
|
link_directories(${EXTRA_LIBS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Enable -fPIC for all targets.
|
||||||
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
check_pie_supported()
|
||||||
|
|
||||||
# Find gettext
|
# Find gettext
|
||||||
find_package(Gettext)
|
find_package(Gettext)
|
||||||
find_package(Intl)
|
find_package(Intl)
|
||||||
if(GETTEXT_FOUND AND Intl_FOUND)
|
if(Gettext_FOUND AND Intl_FOUND)
|
||||||
set(HAVE_LOCALIZATION ON)
|
set(HAVE_LOCALIZATION ON)
|
||||||
include_directories(${Intl_INCLUDE_DIRS})
|
include_directories(${Intl_INCLUDE_DIRS})
|
||||||
link_libraries(${Intl_LIBRARIES})
|
link_libraries(${Intl_LIBRARIES})
|
||||||
else()
|
else()
|
||||||
message("Unable to find gettext and libintl; disabling localization")
|
message(STATUS "Unable to find gettext and/or libintl -- read docs/LANGUAGE for how to enable localization")
|
||||||
set(HAVE_LOCALIZATION OFF)
|
set(HAVE_LOCALIZATION OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -144,28 +149,37 @@ elseif(NOT CMAKE_INSTALL_PREFIX)
|
|||||||
set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/anope-${VERSION_MAJOR}-${VERSION_MINOR}")
|
set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/anope-${VERSION_MAJOR}-${VERSION_MINOR}")
|
||||||
endif()
|
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
|
# Set default paths for various directories if not already defined
|
||||||
if(NOT BIN_DIR)
|
configure_path(BIN_DIR "bin")
|
||||||
set(BIN_DIR "bin")
|
configure_path(DATA_DIR "data")
|
||||||
endif()
|
configure_path(DOC_DIR "doc")
|
||||||
if(NOT DATA_DIR)
|
configure_path(CONF_DIR "conf")
|
||||||
set(DATA_DIR "data")
|
configure_path(MODULE_DIR "modules")
|
||||||
endif()
|
configure_path(LOCALE_DIR "locale")
|
||||||
if(NOT DOC_DIR)
|
configure_path(LOG_DIR "logs")
|
||||||
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()
|
|
||||||
|
|
||||||
# Version number processing
|
# Version number processing
|
||||||
# Find all lines in src/version.sh that start with VERSION_
|
# 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)
|
cmake_path(GET SERVICES_BINARY FILENAME SERVICES_BINARY)
|
||||||
|
|
||||||
# At install time, create the following additional directories
|
# At install time, create the following additional directories
|
||||||
file(REAL_PATH ${DATA_DIR} ABSOLUTE_DATA_DIR BASE_DIRECTORY ${CMAKE_INSTALL_PREFIX})
|
install(DIRECTORY DESTINATION "${DATA_DIR}/backups")
|
||||||
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}\")")
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ABSOLUTE_DATA_DIR}/runtime\")")
|
install(DIRECTORY DESTINATION "${DATA_DIR}/runtime")
|
||||||
endif()
|
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
|
# 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)
|
if(NOT WIN32 AND RUNGROUP)
|
||||||
@@ -238,7 +249,6 @@ if(WIN32)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
file(REAL_PATH ${MODULE_DIR} ABSOLUTE_MODULE_DIR BASE_DIRECTORY ${CMAKE_INSTALL_PREFIX})
|
|
||||||
install(CODE "file(REMOVE_RECURSE \"$ENV{DESTDIR}${ABSOLUTE_MODULE_DIR}\")")
|
install(CODE "file(REMOVE_RECURSE \"$ENV{DESTDIR}${ABSOLUTE_MODULE_DIR}\")")
|
||||||
|
|
||||||
# Only process the CPack section if we have CPack
|
# Only process the CPack section if we have CPack
|
||||||
@@ -289,6 +299,7 @@ endif()
|
|||||||
add_subdirectory(data)
|
add_subdirectory(data)
|
||||||
add_subdirectory(docs)
|
add_subdirectory(docs)
|
||||||
add_subdirectory(language)
|
add_subdirectory(language)
|
||||||
|
add_subdirectory(vendor)
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
add_subdirectory(modules)
|
add_subdirectory(modules)
|
||||||
add_subdirectory(include)
|
add_subdirectory(include)
|
||||||
|
|||||||
@@ -29,43 +29,41 @@ Load_Cache () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Run_Build_System () {
|
Run_Build_System () {
|
||||||
WITH_INST=""
|
BUILD_DIR="${SOURCE_DIR}/build"
|
||||||
WITH_RUN=""
|
CMAKE_COMMAND="${CMAKE:-cmake} -B ${BUILD_DIR} -S ${SOURCE_DIR}"
|
||||||
WITH_PERM=""
|
|
||||||
EXTRA_INCLUDE=""
|
|
||||||
EXTRA_LIBS=""
|
|
||||||
|
|
||||||
if [ "$INSTDIR" != "" ] ; then
|
if [ "$INSTDIR" != "" ] ; then
|
||||||
WITH_INST="-DINSTDIR=$INSTDIR"
|
CMAKE_COMMAND="${CMAKE_COMMAND} -D INSTDIR=$INSTDIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$RUNGROUP" != "" ] ; then
|
if [ "$RUNGROUP" != "" ] ; then
|
||||||
WITH_RUN="-DRUNGROUP=$RUNGROUP"
|
CMAKE_COMMAND="${CMAKE_COMMAND} -D RUNGROUP=$RUNGROUP"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$UMASK" != "" ] ; then
|
if [ "$UMASK" != "" ] ; then
|
||||||
WITH_PERM="-DDEFUMASK=$UMASK"
|
CMAKE_COMMAND="${CMAKE_COMMAND} -D DEFUMASK=$UMASK"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$DEBUG" = "yes" ] ; then
|
if [ "$DEBUG" = "yes" ] ; then
|
||||||
BUILD_TYPE="-DCMAKE_BUILD_TYPE=Debug"
|
CMAKE_COMMAND="${CMAKE_COMMAND} -D CMAKE_BUILD_TYPE=Debug"
|
||||||
else
|
else
|
||||||
BUILD_TYPE="-DCMAKE_BUILD_TYPE=Release"
|
CMAKE_COMMAND="${CMAKE_COMMAND} -D CMAKE_BUILD_TYPE=Release"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$EXTRA_INCLUDE_DIRS" != "" ] ; then
|
if [ "$EXTRA_INCLUDE_DIRS" != "" ] ; then
|
||||||
EXTRA_INCLUDE="-DEXTRA_INCLUDE=$EXTRA_INCLUDE_DIRS"
|
CMAKE_COMMAND="${CMAKE_COMMAND} -D EXTRA_INCLUDE=$EXTRA_INCLUDE_DIRS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$EXTRA_LIB_DIRS" != "" ] ; then
|
if [ "$EXTRA_LIB_DIRS" != "" ] ; then
|
||||||
EXTRA_LIBS="-DEXTRA_LIBS=$EXTRA_LIB_DIRS"
|
CMAKE_COMMAND="${CMAKE_COMMAND} -D EXTRA_LIBS=$EXTRA_LIB_DIRS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BUILD_PATHS="-B ${SOURCE_DIR}/build ${SOURCE_DIR}"
|
if [ "$EXTRA_CONFIG_ARGS" != "" ] ; then
|
||||||
|
CMAKE_COMMAND="${CMAKE_COMMAND} $EXTRA_CONFIG_ARGS"
|
||||||
|
fi
|
||||||
|
|
||||||
CMAKE="cmake $GEN_TYPE $WITH_INST $WITH_RUN $WITH_PERM $BUILD_TYPE $EXTRA_INCLUDE $EXTRA_LIBS $EXTRA_CONFIG_ARGS $BUILD_PATHS"
|
echo $CMAKE_COMMAND
|
||||||
echo $CMAKE
|
$CMAKE_COMMAND
|
||||||
$CMAKE
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "You should fix these issues and then run ./Config -quick to rerun CMake."
|
echo "You should fix these issues and then run ./Config -quick to rerun CMake."
|
||||||
@@ -73,11 +71,7 @@ Run_Build_System () {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
if [ "$PWD" = "${SOURCE_DIR}/build" ]; then
|
echo "Now run 'make -C ${BUILD_DIR#"$PWD/"} install' to build and install Anope."
|
||||||
echo "Now run make to build Anope."
|
|
||||||
else
|
|
||||||
echo "Now cd build, then run make to build Anope."
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
@@ -98,7 +92,12 @@ CAN_QUICK="no"
|
|||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
while [ $# -ge 1 ] ; do
|
while [ $# -ge 1 ] ; do
|
||||||
if [ $1 = "--help" ] ; then
|
OPTION=$1
|
||||||
|
while [ "${OPTION#-}" != "$OPTION" ]; do
|
||||||
|
OPTION="${OPTION#-}"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$OPTION" = "--help" ] ; then
|
||||||
echo "Config utility for Anope"
|
echo "Config utility for Anope"
|
||||||
echo "------------------------"
|
echo "------------------------"
|
||||||
echo "Syntax: ./Config [options]"
|
echo "Syntax: ./Config [options]"
|
||||||
@@ -106,15 +105,15 @@ while [ $# -ge 1 ] ; do
|
|||||||
echo "-nointro Skip intro (disclaimer, etc)"
|
echo "-nointro Skip intro (disclaimer, etc)"
|
||||||
echo "-quick Skip questions, go straight to cmake"
|
echo "-quick Skip questions, go straight to cmake"
|
||||||
exit 0
|
exit 0
|
||||||
elif [ $1 = "-devel" ] ; then
|
elif [ "$OPTION" = "devel" ] ; then
|
||||||
DEBUG="yes"
|
DEBUG="yes"
|
||||||
DEVEL="yes"
|
DEVEL="yes"
|
||||||
INSTDIR="$SOURCE_DIR/run"
|
INSTDIR="$SOURCE_DIR/run"
|
||||||
elif [ $1 = "-nocache" ] ; then
|
elif [ "$OPTION" = "nocache" ] ; then
|
||||||
IGNORE_CACHE="1"
|
IGNORE_CACHE="1"
|
||||||
elif [ $1 = "-nointro" ] ; then
|
elif [ "$OPTION" = "nointro" ] ; then
|
||||||
NO_INTRO="1"
|
NO_INTRO="1"
|
||||||
elif [ $1 = "-quick" -o $1 = "-q" ] ; then
|
elif [ "$OPTION" = "quick" -o "$OPTION" = "q" ] ; then
|
||||||
Load_Cache
|
Load_Cache
|
||||||
if [ "$CAN_QUICK" = "yes" ] ; then
|
if [ "$CAN_QUICK" = "yes" ] ; then
|
||||||
Run_Build_System
|
Run_Build_System
|
||||||
|
|||||||
+2
-22
@@ -1228,7 +1228,7 @@ module
|
|||||||
* An optional prefix to prepended to the name of each created table.
|
* An optional prefix to prepended to the name of each created table.
|
||||||
* Do not use the same prefix for other programs.
|
* Do not use the same prefix for other programs.
|
||||||
*/
|
*/
|
||||||
#prefix = "anope_db_"
|
#prefix = "anope21_"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Whether or not to import data from another database module in to SQL on
|
* Whether or not to import data from another database module in to SQL on
|
||||||
@@ -1248,26 +1248,6 @@ module
|
|||||||
import = no
|
import = no
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* db_redis.
|
|
||||||
*
|
|
||||||
* This module allows using Redis (https://redis.io/) as a database backend.
|
|
||||||
* This module requires that redis is loaded and configured properly.
|
|
||||||
*
|
|
||||||
* Redis 2.8 supports keyspace notifications which allows Redis to push notifications
|
|
||||||
* to Anope about outside modifications to the database. This module supports this and
|
|
||||||
* will internally reflect any changes made to the database immediately once notified.
|
|
||||||
* See docs/REDIS for more information regarding this.
|
|
||||||
*/
|
|
||||||
#module
|
|
||||||
{
|
|
||||||
name = "db_redis"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Redis database to use. This must be configured with redis.
|
|
||||||
*/
|
|
||||||
engine = "redis/main"
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* [RECOMMENDED] Encryption modules.
|
* [RECOMMENDED] Encryption modules.
|
||||||
@@ -1316,7 +1296,7 @@ module
|
|||||||
/** The memory hardness in kibibytes of the Argon2 algorithm. Defaults to
|
/** The memory hardness in kibibytes of the Argon2 algorithm. Defaults to
|
||||||
* 128 mebibytes.
|
* 128 mebibytes.
|
||||||
*/
|
*/
|
||||||
#memory_cost = 121072
|
#memory_cost = 131072
|
||||||
|
|
||||||
/** The time hardness (iterations) of the Argon2 algorithm. Defaults to 3.
|
/** The time hardness (iterations) of the Argon2 algorithm. Defaults to 3.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ privilege
|
|||||||
rank = 10
|
rank = 10
|
||||||
level = 3
|
level = 3
|
||||||
flag = "f"
|
flag = "f"
|
||||||
|
flag_migration_requires = "ACCESS_CHANGE"
|
||||||
xop = "VOP"
|
xop = "VOP"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,6 +497,7 @@ privilege
|
|||||||
rank = 110
|
rank = 110
|
||||||
level = 4
|
level = 4
|
||||||
flag = "h"
|
flag = "h"
|
||||||
|
flag_migration_requires = "HALFOP"
|
||||||
xop = "HOP"
|
xop = "HOP"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,6 +640,7 @@ privilege
|
|||||||
rank = 220
|
rank = 220
|
||||||
level = 5
|
level = 5
|
||||||
flag = "o"
|
flag = "o"
|
||||||
|
flag_migration_requires = "OP"
|
||||||
xop = "AOP"
|
xop = "AOP"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,6 +713,7 @@ privilege
|
|||||||
rank = 300
|
rank = 300
|
||||||
level = 10
|
level = 10
|
||||||
flag = "a"
|
flag = "a"
|
||||||
|
flag_migration_requires = "PROTECT"
|
||||||
xop = "SOP"
|
xop = "SOP"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -853,6 +857,7 @@ privilege
|
|||||||
rank = 60
|
rank = 60
|
||||||
level = 3
|
level = 3
|
||||||
flag = "v"
|
flag = "v"
|
||||||
|
flag_migration_requires = "VOICE"
|
||||||
xop = "VOP"
|
xop = "VOP"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ module
|
|||||||
* An optional prefix to prepended to the name of each created table.
|
* An optional prefix to prepended to the name of each created table.
|
||||||
* Do not use the same prefix for other programs.
|
* Do not use the same prefix for other programs.
|
||||||
*/
|
*/
|
||||||
prefix = "anope_"
|
#prefix = "chanstats21_"
|
||||||
|
|
||||||
smileyshappy = ":) :-) ;) ;-) :D :-D :P :-P"
|
smileyshappy = ":) :-) ;) ;-) :D :-D :P :-P"
|
||||||
smileyssad = ":( :-( ;( ;-("
|
smileyssad = ":( :-( ;( ;-("
|
||||||
|
|||||||
@@ -201,6 +201,19 @@ module
|
|||||||
{
|
{
|
||||||
name = "hs_request"
|
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
|
* If set, Anope will send a memo to the user requesting a vhost when it's been
|
||||||
* approved or rejected.
|
* approved or rejected.
|
||||||
|
|||||||
@@ -374,30 +374,6 @@ module { name = "help" }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* redis
|
|
||||||
*
|
|
||||||
* This module allows other modules to use Redis.
|
|
||||||
*/
|
|
||||||
#module
|
|
||||||
{
|
|
||||||
name = "redis"
|
|
||||||
|
|
||||||
/* A redis database */
|
|
||||||
redis
|
|
||||||
{
|
|
||||||
/* The name of this service */
|
|
||||||
name = "redis/main"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The redis database to use. New connections default to 0.
|
|
||||||
*/
|
|
||||||
db = 0
|
|
||||||
|
|
||||||
ip = "127.0.0.1"
|
|
||||||
port = 6379
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* [EXTRA] regex_pcre2
|
* [EXTRA] regex_pcre2
|
||||||
|
|||||||
@@ -120,6 +120,10 @@ module
|
|||||||
* - ns_keep_modes: Enables keepmodes, which retains user modes across sessions
|
* - ns_keep_modes: Enables keepmodes, which retains user modes across sessions
|
||||||
* - protect: Protects the registered nickname from use by unidentified users.
|
* - protect: Protects the registered nickname from use by unidentified users.
|
||||||
* - ns_stats: Enable Chanstats for newly registered nicks
|
* - 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
|
* 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.
|
* memo_receive. If you really want no defaults, use "none" by itself as the option.
|
||||||
@@ -128,9 +132,9 @@ module
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* The minimum length of time between consecutive uses of NickServ's REGISTER command. This
|
* The minimum length of time between consecutive uses of NickServ's REGISTER command. This
|
||||||
* directive is optional, but recommended. If not set, this restriction will be disabled.
|
* directive is optional, but recommended. If not set, it defaults to 15 minutes.
|
||||||
*/
|
*/
|
||||||
regdelay = 5m
|
regdelay = 15m
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The length of time before a nick's registration expires.
|
* The length of time before a nick's registration expires.
|
||||||
@@ -416,6 +420,12 @@ module
|
|||||||
{
|
{
|
||||||
name = "ns_group"
|
name = "ns_group"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The minimum length of time between consecutive uses of the GROUP command. This directive is
|
||||||
|
* optional, but recommended. If not set, it defaults to 5 minutes.
|
||||||
|
*/
|
||||||
|
delay = 5m
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The maximum number of nicks allowed in a group.
|
* The maximum number of nicks allowed in a group.
|
||||||
*
|
*
|
||||||
@@ -741,6 +751,9 @@ command { service = "NickServ"; name = "SASET LAYOUT"; command = "nickserv/saset
|
|||||||
*
|
*
|
||||||
* misc_description: A description of the command to show in the help.
|
* misc_description: A description of the command to show in the help.
|
||||||
* misc_title: A human-readable description of the stored data.
|
* misc_title: A human-readable description of the stored data.
|
||||||
|
* misc_priority: Positive integer representing display order in nickserv/info
|
||||||
|
* and (if enabled) WHOIS output. Entries with unspecified
|
||||||
|
* priority will be prioritized in the order of declaration.
|
||||||
* misc_pattern: If defined then a regex pattern (using the engine specified
|
* misc_pattern: If defined then a regex pattern (using the engine specified
|
||||||
* in <options:regexengine> to validate the data with).
|
* in <options:regexengine> to validate the data with).
|
||||||
* misc_syntax: If defined then the syntax to show in the help output and, if
|
* misc_syntax: If defined then the syntax to show in the help output and, if
|
||||||
|
|||||||
+3
-3
@@ -3,7 +3,7 @@ and other useful contributions to Anope. These people, ordered by the number of
|
|||||||
contributions they have made, are:
|
contributions they have made, are:
|
||||||
|
|
||||||
* Adam <adam@anope.org>
|
* Adam <adam@anope.org>
|
||||||
* Sadie Powell <sadie@witchery.services>
|
* Sadie Powell <sadie@sadiepowell.dev>
|
||||||
* Robin Burchell <w00t@inspircd.org>
|
* Robin Burchell <w00t@inspircd.org>
|
||||||
* Naram Qashat <cyberbotx@anope.org>
|
* Naram Qashat <cyberbotx@anope.org>
|
||||||
* Pieter Bootsma <geniusdex@anope.org>
|
* Pieter Bootsma <geniusdex@anope.org>
|
||||||
@@ -20,8 +20,8 @@ contributions they have made, are:
|
|||||||
* Adam Kramer <ribosome@anope.org>
|
* Adam Kramer <ribosome@anope.org>
|
||||||
* Attila Molnar <attilamolnar@hush.com>
|
* Attila Molnar <attilamolnar@hush.com>
|
||||||
* Michael Wobst <wobst.michael@web.de>
|
* Michael Wobst <wobst.michael@web.de>
|
||||||
* Matt Schatz <genius3000@g3k.solutions>
|
|
||||||
* PeGaSuS <droider.pc@gmail.com>
|
* PeGaSuS <droider.pc@gmail.com>
|
||||||
|
* Matt Schatz <genius3000@g3k.solutions>
|
||||||
* Mark Summers <mark@goopler.net>
|
* Mark Summers <mark@goopler.net>
|
||||||
* Daniel Vassdal <shutter@canternet.org>
|
* Daniel Vassdal <shutter@canternet.org>
|
||||||
* MatthewM <mcm@they-got.us>
|
* MatthewM <mcm@they-got.us>
|
||||||
@@ -49,6 +49,7 @@ contributions they have made, are:
|
|||||||
* Robert Scheck <robert@fedoraproject.org>
|
* Robert Scheck <robert@fedoraproject.org>
|
||||||
* Dennis Friis <peavey@inspircd.org>
|
* Dennis Friis <peavey@inspircd.org>
|
||||||
* Filippo Cortigiani <simos@simosnap.org>
|
* Filippo Cortigiani <simos@simosnap.org>
|
||||||
|
* KidProtect
|
||||||
* Michał Zegan <webczat_200@poczta.onet.pl>
|
* Michał Zegan <webczat_200@poczta.onet.pl>
|
||||||
* AlphaTech <alphat3ch@icloud.com>
|
* AlphaTech <alphat3ch@icloud.com>
|
||||||
* Austin Ellis <siniStar@IRC4Fun.net>
|
* Austin Ellis <siniStar@IRC4Fun.net>
|
||||||
@@ -71,7 +72,6 @@ contributions they have made, are:
|
|||||||
* Jason Foster <retsofaj@gmail.com>
|
* Jason Foster <retsofaj@gmail.com>
|
||||||
* Jeremy <jeremy@ssnet.ca>
|
* Jeremy <jeremy@ssnet.ca>
|
||||||
* Josh Soref
|
* Josh Soref
|
||||||
* KidProtect
|
|
||||||
* KindOne <ineedalifetoday@live.com>
|
* KindOne <ineedalifetoday@live.com>
|
||||||
* linuxdaemon
|
* linuxdaemon
|
||||||
* Mantas MikulÄ—nas <grawity@gmail.com>
|
* Mantas MikulÄ—nas <grawity@gmail.com>
|
||||||
|
|||||||
@@ -1,5 +1,73 @@
|
|||||||
# Anope Change Log
|
# Anope Change Log
|
||||||
|
|
||||||
|
## Anope 2.1.25 (unreleased)
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Fixed a crash when a temporary ban expires.
|
||||||
|
|
||||||
|
* Fixed a potential crash caused by unusual timestamps being passed to `Anope::strftime`.
|
||||||
|
|
||||||
|
* Fixed confirming accounts when `{ns_register}:registration` is set to "admin".
|
||||||
|
|
||||||
|
* Fixed migrating access entries from xop to flags granting new privileges in cases where multiple privileges share the same flag.
|
||||||
|
|
||||||
|
* Fixed migrating access entries sometimes giving the wrong flags.
|
||||||
|
|
||||||
|
* Fixed not being able to update the description of a flags access entry.
|
||||||
|
|
||||||
|
## Anope 2.1.24 (2026-04-01)
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* If a database contains duplicate corrupt entries from a prior write failure the oldest ones will now be purged from the database. This is a destructive action so make sure you take a manual backup of your database before upgrading.
|
||||||
|
|
||||||
|
* Removed support for storing the Anope database in Redis. The Redis code was extremely bitrotted, had not been tested in years, and to our knowledge has almost no (if any) users. It is recommended that db_redis users migrate to db_json or db_sql.
|
||||||
|
|
||||||
|
* SQL tables now use versioned prefixes by default. For the SQL database backends the default is `anope21_` and for ChanStats the default is `chanstats21_`. If you do not have a prefix explicitly set in your config you will need to add one it. Alternatively, you may also want to consider exporting to db_json and re-importing to update your SQL schema for the recent database layout changes.
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Added some helper methods to `CommandSource` to allow quickly translting messages.
|
||||||
|
|
||||||
|
* Changed the Config script to allow multiple dashes in front of options, i.e. `-quick` and `--quick` are now equivalent.
|
||||||
|
|
||||||
|
* Converted some language strings to use format strings instead of concatenation.
|
||||||
|
|
||||||
|
* Fixed a rare crash in the ns_cert module.
|
||||||
|
|
||||||
|
* Fixed building Anope as a unity build.
|
||||||
|
|
||||||
|
* Fixed the ns_cert module erasing certificate entries if using an old database.
|
||||||
|
|
||||||
|
* Fixed users having the wrong real name in log messages on InspIRCd if it has been previously changed with `CHGNAME` or `SETNAME`.
|
||||||
|
|
||||||
|
## Anope 2.1.23 (2026-04-01)
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Added examples to several BotServ commands.
|
||||||
|
|
||||||
|
* Added missing fields to the `RPL_STATSLINKINFO` output.
|
||||||
|
|
||||||
|
* Added support for migrating access entries between systems (currently only `chanserv/flags` is supported).
|
||||||
|
|
||||||
|
* Added the default levels to the `chanserv/levels` DESC help.
|
||||||
|
|
||||||
|
* Changed access listing commands to only show their own access entries unless `ALL` is specified.
|
||||||
|
|
||||||
|
* Fixed a non-translatable string which has been marked as translatable.
|
||||||
|
|
||||||
|
* Fixed the missing AUTOLOGIN extension.
|
||||||
|
|
||||||
|
* Fixed translating the help output when the flexible layout is used.
|
||||||
|
|
||||||
|
* Improved the accuracy of the X-line expiry time in `operserv/stats`.
|
||||||
|
|
||||||
|
* Updated the Portuguese translation.
|
||||||
|
|
||||||
|
* Updated the Romanian translation.
|
||||||
|
|
||||||
## Anope 2.1.22 (2026-03-01)
|
## Anope 2.1.22 (2026-03-01)
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|||||||
+1
-2
@@ -32,8 +32,7 @@ Anope Multi Language Support
|
|||||||
but 0x1B is special to Anope and is used to prevent the automatic linewrapper from breaking messages in the middle of
|
but 0x1B is special to Anope and is used to prevent the automatic linewrapper from breaking messages in the middle of
|
||||||
text that should not be split (e.g. commands). Your editor may not show these so be careful you don't delete them!
|
text that should not be split (e.g. commands). Your editor may not show these so be careful you don't delete them!
|
||||||
|
|
||||||
If you have finished a language file translation and you want others to use it, please file a pull request on GitHub
|
If you have finished a language file translation and you want others to use it, please file a pull request on GitHub.
|
||||||
or send it to team@anope.org (don't forget to mention clearly your (nick)name, your email and the language name).
|
|
||||||
You'll of course get full credit for it.
|
You'll of course get full credit for it.
|
||||||
|
|
||||||
3) Using languages with modules
|
3) Using languages with modules
|
||||||
|
|||||||
-160
@@ -1,160 +0,0 @@
|
|||||||
Anope has Redis database support (https://redis.io/).
|
|
||||||
This document explains the data structure used by Anope, and explains how
|
|
||||||
keyspace notification works.
|
|
||||||
|
|
||||||
This is not a tutorial on how to use Redis, see https://redis.io/documentation
|
|
||||||
for that.
|
|
||||||
|
|
||||||
Table of Contents
|
|
||||||
-----------------
|
|
||||||
1) Data structure
|
|
||||||
2) Keyspace notifications
|
|
||||||
3) Examples of modifying, deleting, and creating objects
|
|
||||||
|
|
||||||
1) Data structure
|
|
||||||
|
|
||||||
There are 4 key namespaces in Anope, they are:
|
|
||||||
|
|
||||||
id - The keys in id are used to atomically create object ids for new
|
|
||||||
objects. For example, if I were to create a new BotInfo I would first:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> INCR id:BotInfo
|
|
||||||
|
|
||||||
To get the object ID of the new object.
|
|
||||||
|
|
||||||
ids - The keys in ids contain a set of all object ids of the given type.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> SMEMBERS ids:BotInfo
|
|
||||||
|
|
||||||
Returns "1", "2", "3", "4", "5", "6", "7", "8" because I have 8 bots that
|
|
||||||
have IDs 1, 2, 3, 4, 5, 6, 7, and 8, respectively.
|
|
||||||
|
|
||||||
hash - The keys in hash are the actual objects, stored as hashes. For
|
|
||||||
example, if I had just looked up all BotInfo ids and wanted to iterate
|
|
||||||
over all of them, I would start by:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> HGETALL hash:BotInfo:1
|
|
||||||
|
|
||||||
Which gets all keys and values from the hash of type BotInfo with id 1.
|
|
||||||
This may return:
|
|
||||||
|
|
||||||
"nick" -> "BotServ"
|
|
||||||
"user" -> "services"
|
|
||||||
"host" -> "services.anope.org"
|
|
||||||
"created" -> "1368704765"
|
|
||||||
|
|
||||||
value - The keys in value only exist to aid looking up object IDs. They
|
|
||||||
are sets of object IDs and are used to map key+value pairs to objects.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> SMEMBERS value:NickAlias:nick:Adam
|
|
||||||
|
|
||||||
Returns a set of object ids of NickAlias objects that have the key
|
|
||||||
'nick' set to the value 'Adam' in its hash. Clearly this can only
|
|
||||||
ever contain at most one object, since it is not possible to have
|
|
||||||
more than one registered nick with the same name, but other keys
|
|
||||||
will contain more than one, such as:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> SMEMBERS value:NickCore:email:adam@anope.org
|
|
||||||
|
|
||||||
Which would return all accounts with the email "adam@anope.org".
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> SMEMBERS value:ChanAccess:mask:Adam
|
|
||||||
|
|
||||||
Which would return all access entries set on the account "Adam".
|
|
||||||
|
|
||||||
Behavior similar to SQL's AND, can be achieved using the
|
|
||||||
SINTER command, which does set intersection on one or more sets.
|
|
||||||
|
|
||||||
2) Keyspace notifications
|
|
||||||
|
|
||||||
Redis 2.7 (unstable) and 2.8 (stable) and newer support keyspace notifications
|
|
||||||
(https://redis.io/topics/notifications). This allows Redis to notify Anope of
|
|
||||||
any external changes to objects in the database. Once notified, Anope will
|
|
||||||
immediately update the object. Otherwise, Anope keeps all objects in memory
|
|
||||||
and will not regularly read from the database once started.
|
|
||||||
|
|
||||||
You can use this to modify objects in Redis and have them immediately reflected
|
|
||||||
back into Anope. Additionally you can use this feature to run multiple Anope
|
|
||||||
instances simultaneously from the same database (see also, Redis database
|
|
||||||
replication).
|
|
||||||
|
|
||||||
To use keyspace notifications you MUST execute
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> CONFIG SET notify-keyspace-events KA
|
|
||||||
OK
|
|
||||||
|
|
||||||
or set notify-keyspace-events in redis.conf properly. Anope always executes
|
|
||||||
CONFIG SET when it first connects.
|
|
||||||
|
|
||||||
If you do not enable keyspace events properly Anope will be UNABLE to see any
|
|
||||||
object modifications you do.
|
|
||||||
|
|
||||||
The key space ids and value are managed entirely by Anope, you do
|
|
||||||
not (and should not) modify them. Once you modify the object (hash), Anope will
|
|
||||||
update them for you to correctly reflect any changes made to the object.
|
|
||||||
|
|
||||||
Finally, always use atomic operations. If you are inserting a new object with
|
|
||||||
multiple commands, or inserting multiple objects at once, specifically if the
|
|
||||||
objects depend on each other, you MUST use a transaction.
|
|
||||||
|
|
||||||
3) Examples of modifying, deleting, and creating objects
|
|
||||||
|
|
||||||
These examples will ONLY work if you meet the criteria in section 2.
|
|
||||||
|
|
||||||
If I want to change the email account 'Adam' to 'Adam@anope.org', I would execute the following:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> SMEMBERS value:NickCore:display:Adam
|
|
||||||
|
|
||||||
Which returns a value of "1", which is the object id I want to modify.
|
|
||||||
Now to change the email:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> HSET hash:NickCore:1 email Adam@anope.org
|
|
||||||
|
|
||||||
You can now see this in NickServ's INFO command:
|
|
||||||
-NickServ- Email address: Adam@anope.org
|
|
||||||
|
|
||||||
If I want to drop the account "Adam", I would execute the following:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> SMEMBERS value:NickCore:display:Adam
|
|
||||||
|
|
||||||
Which returns a value of "1". I would then check:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> SMEMBERS value:NickAlias:nc:Adam
|
|
||||||
|
|
||||||
To see what nicknames depend on this account to exist, as I will
|
|
||||||
have to remove those too. This returns the values "2", and "3".
|
|
||||||
|
|
||||||
Finally, I can drop the nick using a transaction via:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> MULTI
|
|
||||||
OK
|
|
||||||
redis 127.0.0.1:6379> DEL hash:NickAlias:2
|
|
||||||
QUEUED
|
|
||||||
redis 127.0.0.1:6379> DEL hash:NickAlias:3
|
|
||||||
QUEUED
|
|
||||||
redis 127.0.0.1:6379> DEL hash:NickCore:1
|
|
||||||
QUEUED
|
|
||||||
redis 127.0.0.1:6379> EXEC
|
|
||||||
|
|
||||||
Or alternatively simply:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> DEL hash:NickAlias:2 hash:NickAlias:3 hash:NickCore:1
|
|
||||||
|
|
||||||
If I wanted to create a BotServ bot, I would execute the following:
|
|
||||||
|
|
||||||
redis 127.0.0.1:6379> INCR id:BotInfo
|
|
||||||
|
|
||||||
Which returns a new object ID for me, in this example it will be "8".
|
|
||||||
Now I can create the object:
|
|
||||||
|
|
||||||
HMSET hash:BotInfo:8 nick redis user redis host services.anope.org realname "Anope IRC Services"
|
|
||||||
|
|
||||||
Note if you are using HSET instead of HMSET you will need to use a transaction, as shown in the above example.
|
|
||||||
If you are watching your services logs you will immediately see:
|
|
||||||
|
|
||||||
USERS: redis!redis@services.anope.org (Anope IRC Services) connected to the network (services.anope.org)
|
|
||||||
|
|
||||||
And the bot redis will be in BotServ's bot list.
|
|
||||||
Notice how ids:BotInfo and the value keys are updated automatically.
|
|
||||||
@@ -67,6 +67,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual ChanAccess *Create() = 0;
|
virtual ChanAccess *Create() = 0;
|
||||||
|
|
||||||
|
virtual void GetAccess(CommandSource& source, const Privilege *p, Anope::map<Anope::string> &access) = 0;
|
||||||
|
static void SendAccess(CommandSource &source, const Anope::string &pname);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::list<AccessProvider *> Providers;
|
static std::list<AccessProvider *> Providers;
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -362,10 +362,6 @@ namespace Anope
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
using unordered_map = std::unordered_map<string, T, hash_ci, compare>;
|
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.
|
/** The time Anope started.
|
||||||
*/
|
*/
|
||||||
extern CoreExport time_t StartTime;
|
extern CoreExport time_t StartTime;
|
||||||
|
|||||||
+6
-1
@@ -90,6 +90,11 @@ public:
|
|||||||
void Reply(int count, const char *singular, const char *plural, ...) ATTR_FORMAT(4, 5);
|
void Reply(int count, const char *singular, const char *plural, ...) ATTR_FORMAT(4, 5);
|
||||||
void Reply(const Anope::string &message);
|
void Reply(const Anope::string &message);
|
||||||
|
|
||||||
|
const char *Translate(const char *message);
|
||||||
|
const char *Translate(const Anope::string &message);
|
||||||
|
const char *Translate(int count, const char *single, const char *plural);
|
||||||
|
const char *Translate(int count, const Anope::string &single, const Anope::string &plural);
|
||||||
|
|
||||||
bool HasCommand(const Anope::string &cmd);
|
bool HasCommand(const Anope::string &cmd);
|
||||||
bool HasPriv(const Anope::string &cmd);
|
bool HasPriv(const Anope::string &cmd);
|
||||||
bool IsServicesOper();
|
bool IsServicesOper();
|
||||||
@@ -189,5 +194,5 @@ public:
|
|||||||
* @param name If found, is set to the command name, eg REGISTER
|
* @param name If found, is set to the command name, eg REGISTER
|
||||||
* @return true if the given command service exists
|
* @return true if the given command service exists
|
||||||
*/
|
*/
|
||||||
static bool FindCommandFromService(const Anope::string &command_service, BotInfo *&bi, Anope::string &name);
|
static bool FindFromService(const Anope::string &command_service, BotInfo *&bi, Anope::string &name);
|
||||||
};
|
};
|
||||||
|
|||||||
+13
-10
@@ -18,6 +18,7 @@
|
|||||||
#include "regchannel.h"
|
#include "regchannel.h"
|
||||||
#include "users.h"
|
#include "users.h"
|
||||||
#include "opertype.h"
|
#include "opertype.h"
|
||||||
|
#include "miscutils.h"
|
||||||
|
|
||||||
namespace Configuration
|
namespace Configuration
|
||||||
{
|
{
|
||||||
@@ -26,14 +27,15 @@ namespace Configuration
|
|||||||
friend class Configuration::Conf;
|
friend class Configuration::Conf;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef Anope::map<Anope::string> item_map;
|
typedef Anope::map<Anope::string> ItemMap;
|
||||||
typedef Anope::multimap<Block> block_map;
|
typedef Anope::multimap<Block> BlockMap;
|
||||||
|
typedef Anope::iterator_range<BlockMap::const_iterator> BlockList;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Anope::string name;
|
Anope::string name;
|
||||||
item_map items;
|
ItemMap items;
|
||||||
block_map blocks;
|
BlockMap blocks;
|
||||||
int linenum;
|
unsigned linenum;
|
||||||
|
|
||||||
/* Represents a missing tag. */
|
/* Represents a missing tag. */
|
||||||
static Block EmptyBlock;
|
static Block EmptyBlock;
|
||||||
@@ -41,9 +43,10 @@ namespace Configuration
|
|||||||
public:
|
public:
|
||||||
Block(const Anope::string &);
|
Block(const Anope::string &);
|
||||||
const Anope::string &GetName() const;
|
const Anope::string &GetName() const;
|
||||||
int CountBlock(const Anope::string &name) const;
|
size_t CountBlock(const Anope::string &name) const;
|
||||||
const Block &GetBlock(const Anope::string &name, int num = 0) const;
|
BlockList GetBlocks(const Anope::string &name) const;
|
||||||
Block *GetMutableBlock(const Anope::string &name, int num = 0);
|
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
|
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);
|
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;
|
template<> CoreExport const Anope::string Block::Get(const Anope::string &tag, const Anope::string &def) const;
|
||||||
@@ -85,7 +88,7 @@ namespace Configuration
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/** Replaces defined variables within a string. */
|
/** 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:
|
public:
|
||||||
/* options:readtimeout */
|
/* options:readtimeout */
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ public:
|
|||||||
void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) override
|
void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) override
|
||||||
{
|
{
|
||||||
T t;
|
T t;
|
||||||
if (data[this->name] >> t)
|
if (data.TryLoad(this->name, t))
|
||||||
this->Set(e, t);
|
this->Set(e, t);
|
||||||
else
|
else
|
||||||
this->Unset(e);
|
this->Unset(e);
|
||||||
@@ -205,9 +205,7 @@ public:
|
|||||||
|
|
||||||
void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) override
|
void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) override
|
||||||
{
|
{
|
||||||
bool b = false;
|
if (data.Load<bool>(this->name))
|
||||||
data[this->name] >> b;
|
|
||||||
if (b)
|
|
||||||
this->Set(e);
|
this->Set(e);
|
||||||
else
|
else
|
||||||
this->Unset(e);
|
this->Unset(e);
|
||||||
|
|||||||
+2
-1
@@ -44,7 +44,7 @@ namespace Language
|
|||||||
/** Sets the locale to the specified language.
|
/** Sets the locale to the specified language.
|
||||||
* @param lang The language to translate to.
|
* @param lang The language to translate to.
|
||||||
*/
|
*/
|
||||||
extern CoreExport void SetLocale(const char* lang);
|
extern CoreExport void SetLocale(const char *lang);
|
||||||
|
|
||||||
/** Sets the locale back to the default. */
|
/** Sets the locale back to the default. */
|
||||||
extern CoreExport void ResetLocale();
|
extern CoreExport void ResetLocale();
|
||||||
@@ -141,6 +141,7 @@ namespace Language
|
|||||||
#define CHAN_ACCESS_LIMIT_DEEP N_("You can only have %u access entry on a channel, including access entries from other channels.", "You can only have %u access entries on a channel, including access entries from other channels.")
|
#define CHAN_ACCESS_LIMIT_DEEP N_("You can only have %u access entry on a channel, including access entries from other channels.", "You can only have %u access entries on a channel, including access entries from other channels.")
|
||||||
#define CHAN_ACCESS_LEVEL_RANGE _("Access level must be between %d and %d inclusive.")
|
#define CHAN_ACCESS_LEVEL_RANGE _("Access level must be between %d and %d inclusive.")
|
||||||
#define CHAN_ACCESS_MALFORMED _("You cannot add a malformed mask to an access list. Did you mean to add %s instead?")
|
#define CHAN_ACCESS_MALFORMED _("You cannot add a malformed mask to an access list. Did you mean to add %s instead?")
|
||||||
|
#define CHAN_ACCESS_FOREIGN N_("%u access entry from other access systems not shown; use \002%s\033ALL\002 to view all access entries.", "%u access entries from other access systems not shown; use \002%s\033ALL\002 to view all access entries.")
|
||||||
#define CHAN_EXCEPTED _("\002%s\002 matches an except on %s and cannot be banned until the except has been removed.")
|
#define CHAN_EXCEPTED _("\002%s\002 matches an except on %s and cannot be banned until the except has been removed.")
|
||||||
#define CHAN_INFO_HEADER _("Information about channel \002%s\002:")
|
#define CHAN_INFO_HEADER _("Information about channel \002%s\002:")
|
||||||
#define CHAN_LIMIT_EXCEEDED _("You have already exceeded your limit of \002%d\002 channels.")
|
#define CHAN_LIMIT_EXCEEDED _("You have already exceeded your limit of \002%d\002 channels.")
|
||||||
|
|||||||
@@ -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); }
|
||||||
|
};
|
||||||
+4
-2
@@ -601,15 +601,17 @@ public:
|
|||||||
* @param ci The channel
|
* @param ci The channel
|
||||||
* @param source The source of the command
|
* @param source The source of the command
|
||||||
* @param access The access entry that was removed
|
* @param access The access entry that was removed
|
||||||
|
* @param migrated Whether the access entry was deleted because of being migrated to another system.
|
||||||
*/
|
*/
|
||||||
virtual void OnAccessDel(ChannelInfo *ci, CommandSource &source, ChanAccess *access) ATTR_NOT_NULL(2, 4) { throw NotImplementedException(); }
|
virtual void OnAccessDel(ChannelInfo *ci, CommandSource &source, ChanAccess *access, bool migrated) ATTR_NOT_NULL(2, 4) { throw NotImplementedException(); }
|
||||||
|
|
||||||
/** Called when access is added
|
/** Called when access is added
|
||||||
* @param ci The channel
|
* @param ci The channel
|
||||||
* @param source The source of the command
|
* @param source The source of the command
|
||||||
* @param access The access changed
|
* @param access The access changed
|
||||||
|
* @param migrated Whether the access entry was added because of being migrated to another system.
|
||||||
*/
|
*/
|
||||||
virtual void OnAccessAdd(ChannelInfo *ci, CommandSource &source, ChanAccess *access) ATTR_NOT_NULL(2, 4) { throw NotImplementedException(); }
|
virtual void OnAccessAdd(ChannelInfo *ci, CommandSource &source, ChanAccess *access, bool migrated) ATTR_NOT_NULL(2, 4) { throw NotImplementedException(); }
|
||||||
|
|
||||||
/** Called when the access list is cleared
|
/** Called when the access list is cleared
|
||||||
* @param ci The channel
|
* @param ci The channel
|
||||||
|
|||||||
@@ -194,12 +194,13 @@ namespace DNS
|
|||||||
/** Used to time out the query, xalls OnError and lets the TimerManager
|
/** Used to time out the query, xalls OnError and lets the TimerManager
|
||||||
* delete this request.
|
* delete this request.
|
||||||
*/
|
*/
|
||||||
void Tick() override
|
bool Tick() override
|
||||||
{
|
{
|
||||||
Log(LOG_DEBUG_2) << "Resolver: timeout for query " << this->name;
|
Log(LOG_DEBUG_2) << "Resolver: timeout for query " << this->name;
|
||||||
Query rr(*this);
|
Query rr(*this);
|
||||||
rr.error = ERROR_TIMEDOUT;
|
rr.error = ERROR_TIMEDOUT;
|
||||||
this->OnError(&rr);
|
this->OnError(&rr);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public:
|
|||||||
/** Retrieves the size of the messages queue for the specified user.
|
/** Retrieves the size of the messages queue for the specified user.
|
||||||
* @param nc The account to count queued messages for.
|
* @param nc The account to count queued messages for.
|
||||||
*/
|
*/
|
||||||
inline size_t CountQueue(NickCore* nc) const
|
inline size_t CountQueue(NickCore *nc) const
|
||||||
{
|
{
|
||||||
auto *q = GetQueue(nc);
|
auto *q = GetQueue(nc);
|
||||||
return q ? q->size() : 0;
|
return q ? q->size() : 0;
|
||||||
@@ -55,7 +55,7 @@ public:
|
|||||||
/** Retrieves the messages queue for the specified user.
|
/** Retrieves the messages queue for the specified user.
|
||||||
* @param nc The account to retrieve queued messages for.
|
* @param nc The account to retrieve queued messages for.
|
||||||
*/
|
*/
|
||||||
virtual const std::vector<Anope::string> *GetQueue(NickCore* nc) const = 0;
|
virtual const std::vector<Anope::string> *GetQueue(NickCore *nc) const = 0;
|
||||||
|
|
||||||
/** Queues a message to be sent later.
|
/** Queues a message to be sent later.
|
||||||
* @param nc The account to queue the message for.
|
* @param nc The account to queue the message for.
|
||||||
|
|||||||
@@ -173,4 +173,14 @@ public:
|
|||||||
* @param attributes The attributes to modify
|
* @param attributes The attributes to modify
|
||||||
*/
|
*/
|
||||||
virtual void Modify(LDAPInterface *i, const Anope::string &base, LDAPMods &attributes) = 0;
|
virtual void Modify(LDAPInterface *i, const Anope::string &base, LDAPMods &attributes) = 0;
|
||||||
|
|
||||||
|
/** Escapes a LDAP string for use in a DN.
|
||||||
|
* @param str The string to escape.
|
||||||
|
*/
|
||||||
|
virtual Anope::string EscapeDN(const Anope::string &str) const = 0;
|
||||||
|
|
||||||
|
/** Escapes a LDAP string for use in a search filter.
|
||||||
|
* @param str The string to escape.
|
||||||
|
*/
|
||||||
|
virtual Anope::string EscapeSF(const Anope::string &str) const = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
// 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
|
|
||||||
|
|
||||||
namespace Redis
|
|
||||||
{
|
|
||||||
struct Reply final
|
|
||||||
{
|
|
||||||
enum Type
|
|
||||||
{
|
|
||||||
NOT_PARSED,
|
|
||||||
NOT_OK,
|
|
||||||
OK,
|
|
||||||
INT,
|
|
||||||
BULK,
|
|
||||||
MULTI_BULK
|
|
||||||
}
|
|
||||||
type;
|
|
||||||
|
|
||||||
Reply() { Clear(); }
|
|
||||||
~Reply() { Clear(); }
|
|
||||||
|
|
||||||
void Clear()
|
|
||||||
{
|
|
||||||
type = NOT_PARSED;
|
|
||||||
i = 0;
|
|
||||||
bulk.clear();
|
|
||||||
multi_bulk_size = 0;
|
|
||||||
for (const auto *reply : multi_bulk)
|
|
||||||
delete reply;
|
|
||||||
multi_bulk.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t i;
|
|
||||||
Anope::string bulk;
|
|
||||||
int multi_bulk_size;
|
|
||||||
std::deque<Reply *> multi_bulk;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Module *owner;
|
|
||||||
|
|
||||||
Interface(Module *m) : owner(m) { }
|
|
||||||
virtual ~Interface() = default;
|
|
||||||
|
|
||||||
virtual void OnResult(const Reply &r) = 0;
|
|
||||||
virtual void OnError(const Anope::string &error) { Log(owner) << error; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class Provider
|
|
||||||
: public Service
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Provider(Module *c, const Anope::string &n) : Service(c, "Redis::Provider", n) { }
|
|
||||||
|
|
||||||
virtual bool IsSocketDead() = 0;
|
|
||||||
|
|
||||||
virtual void SendCommand(Interface *i, const std::vector<Anope::string> &cmds) = 0;
|
|
||||||
virtual void SendCommand(Interface *i, const Anope::string &str) = 0;
|
|
||||||
|
|
||||||
virtual bool BlockAndProcess() = 0;
|
|
||||||
|
|
||||||
virtual void Subscribe(Interface *i, const Anope::string &pattern) = 0;
|
|
||||||
virtual void Unsubscribe(const Anope::string &pattern) = 0;
|
|
||||||
|
|
||||||
virtual void StartTransaction() = 0;
|
|
||||||
virtual void CommitTransaction() = 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -183,7 +183,7 @@ private:
|
|||||||
size_t minparams;
|
size_t minparams;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Event(Module *o, const Anope::string& e, size_t mp = 0)
|
Event(Module *o, const Anope::string &e, size_t mp = 0)
|
||||||
: Service(o, RPC_EVENT, e)
|
: Service(o, RPC_EVENT, e)
|
||||||
, minparams(mp)
|
, minparams(mp)
|
||||||
{
|
{
|
||||||
|
|||||||
+13
-26
@@ -23,46 +23,33 @@ namespace SQL
|
|||||||
: public Serialize::Data
|
: public Serialize::Data
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::map<Anope::string, std::stringstream *> Map;
|
Anope::unordered_map<Anope::string> data;
|
||||||
Map data;
|
|
||||||
|
|
||||||
~Data()
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::iostream &operator[](const Anope::string &key) override
|
|
||||||
{
|
|
||||||
std::stringstream *&ss = data[key];
|
|
||||||
if (!ss)
|
|
||||||
ss = new std::stringstream();
|
|
||||||
return *ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Hash() const override
|
size_t Hash() const override
|
||||||
{
|
{
|
||||||
size_t hash = 0;
|
size_t hash = 0;
|
||||||
for (const auto &[_, value] : this->data)
|
for (const auto &[_, value] : this->data)
|
||||||
{
|
{
|
||||||
if (!value->str().empty())
|
if (!value.empty())
|
||||||
hash ^= Anope::hash_cs()(value->str());
|
hash ^= Anope::hash_cs()(value);
|
||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<Anope::string, std::iostream *> GetData() const
|
bool LoadInternal(const Anope::string &key, Anope::string &value) override
|
||||||
{
|
{
|
||||||
std::map<Anope::string, std::iostream *> d;
|
auto it = this->data.find(key);
|
||||||
for (const auto &[key, value] : this->data)
|
if (it == this->data.end())
|
||||||
d[key] = value;
|
return false;
|
||||||
return d;
|
|
||||||
|
value = it->second;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clear()
|
bool StoreInternal(const Anope::string &key, const Anope::string &value) override
|
||||||
{
|
{
|
||||||
for (const auto &[_, value] : this->data)
|
this->data[key] = value;
|
||||||
delete value;
|
return true;
|
||||||
this->data.clear();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+10
-2
@@ -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);
|
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.
|
/* Formats an outgoing message so it can be sent to the IRC server.
|
||||||
* @param message The location to store the formatted message.
|
* @param message The location to store the formatted message.
|
||||||
* @param tags IRCv3 message tags.
|
* @param tags IRCv3 message tags.
|
||||||
@@ -279,7 +287,7 @@ public:
|
|||||||
virtual void SendSVSHold(const Anope::string &, time_t) { }
|
virtual void SendSVSHold(const Anope::string &, time_t) { }
|
||||||
virtual void SendSVSHoldDel(const Anope::string &) { }
|
virtual void SendSVSHoldDel(const Anope::string &) { }
|
||||||
|
|
||||||
virtual void SendSWhois(const MessageSource &source, User *target, const Anope::string &tag, const Anope::string &message) { };
|
virtual void SendSWhois(const MessageSource &source, User *target, const Anope::string &tag, time_t priority, const Anope::string &message) { };
|
||||||
virtual void SendSWhoisDel(const MessageSource &source, User *target, const Anope::string &tag, const Anope::string &message) { }
|
virtual void SendSWhoisDel(const MessageSource &source, User *target, const Anope::string &tag, const Anope::string &message) { }
|
||||||
|
|
||||||
/** Introduces a server to the uplink
|
/** Introduces a server to the uplink
|
||||||
@@ -307,7 +315,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void SendOper(User *u);
|
virtual void SendOper(User *u);
|
||||||
|
|
||||||
virtual void SendClearModes(const MessageSource &user, Channel *c, User* u, const Anope::string &mode) { }
|
virtual void SendClearModes(const MessageSource &user, Channel *c, User *u, const Anope::string &mode) { }
|
||||||
|
|
||||||
virtual bool IsNickValid(const Anope::string &);
|
virtual bool IsNickValid(const Anope::string &);
|
||||||
virtual bool IsChannelValid(const Anope::string &);
|
virtual bool IsChannelValid(const Anope::string &);
|
||||||
|
|||||||
+113
-36
@@ -40,43 +40,11 @@ namespace Serialize
|
|||||||
UINT,
|
UINT,
|
||||||
};
|
};
|
||||||
|
|
||||||
class CoreExport Data
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
std::map<Anope::string, Serialize::DataType> types;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~Data() = default;
|
|
||||||
|
|
||||||
virtual std::iostream &operator[](const Anope::string &key) = 0;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void Store(const Anope::string &key, const T &value)
|
|
||||||
{
|
|
||||||
using Type = std::remove_cv_t<std::remove_reference_t<T>>;
|
|
||||||
|
|
||||||
if constexpr (std::is_same_v<Type, bool>)
|
|
||||||
SetType(key, DataType::BOOL);
|
|
||||||
else if constexpr (std::is_floating_point_v<Type>)
|
|
||||||
SetType(key, DataType::FLOAT);
|
|
||||||
else if constexpr (std::is_integral_v<Type> && std::is_signed_v<Type>)
|
|
||||||
SetType(key, DataType::INT);
|
|
||||||
else if constexpr (std::is_integral_v<Type> && std::is_unsigned_v<Type>)
|
|
||||||
SetType(key, DataType::UINT);
|
|
||||||
|
|
||||||
this->operator[](key) << value;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual size_t Hash() const { throw CoreException("Not supported"); }
|
|
||||||
|
|
||||||
Serialize::DataType GetType(const Anope::string &key) const;
|
|
||||||
void SetType(const Anope::string &key, Serialize::DataType dt);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern void RegisterTypes();
|
extern void RegisterTypes();
|
||||||
extern void CheckTypes();
|
extern void CheckTypes();
|
||||||
extern void CreateTypes();
|
extern void CreateTypes();
|
||||||
|
|
||||||
|
class Data;
|
||||||
class Type;
|
class Type;
|
||||||
template<typename T> class Checker;
|
template<typename T> class Checker;
|
||||||
template<typename T> class Reference;
|
template<typename T> class Reference;
|
||||||
@@ -106,6 +74,8 @@ private:
|
|||||||
size_t last_commit = 0;
|
size_t last_commit = 0;
|
||||||
/* The last time this object was committed to the database */
|
/* The last time this object was committed to the database */
|
||||||
time_t last_commit_time = 0;
|
time_t last_commit_time = 0;
|
||||||
|
/** Whether this object should be committed to the database. */
|
||||||
|
bool should_commit = true;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Serializable(const Anope::string &serialize_type);
|
Serializable(const Anope::string &serialize_type);
|
||||||
@@ -113,6 +83,20 @@ protected:
|
|||||||
|
|
||||||
Serializable &operator=(const Serializable &);
|
Serializable &operator=(const Serializable &);
|
||||||
|
|
||||||
|
template<typename Container,
|
||||||
|
typename Key = typename Container::key_type,
|
||||||
|
typename Value = typename Container::mapped_type>
|
||||||
|
bool InsertUnique(Container &container, const Key &key)
|
||||||
|
{
|
||||||
|
auto res = container.emplace(key, static_cast<Value>(this));
|
||||||
|
if (res.second)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
res.first->second->should_commit = false;
|
||||||
|
res.first->second = static_cast<Value>(this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Id = uint64_t;
|
using Id = uint64_t;
|
||||||
virtual ~Serializable();
|
virtual ~Serializable();
|
||||||
@@ -120,13 +104,13 @@ public:
|
|||||||
/* Unique ID (per type, not globally) for this object */
|
/* Unique ID (per type, not globally) for this object */
|
||||||
Id object_id = 0;
|
Id object_id = 0;
|
||||||
|
|
||||||
/* Only used by redis, to ignore updates */
|
|
||||||
unsigned short redis_ignore = 0;
|
|
||||||
|
|
||||||
/** Marks the object as potentially being updated "soon".
|
/** Marks the object as potentially being updated "soon".
|
||||||
*/
|
*/
|
||||||
void QueueUpdate();
|
void QueueUpdate();
|
||||||
|
|
||||||
|
/** Determines whether the object should be committed to the database. */
|
||||||
|
bool ShouldCommit() const { return this->should_commit; }
|
||||||
|
|
||||||
bool IsCached(Serialize::Data &);
|
bool IsCached(Serialize::Data &);
|
||||||
void UpdateCache(Serialize::Data &);
|
void UpdateCache(Serialize::Data &);
|
||||||
|
|
||||||
@@ -137,10 +121,103 @@ public:
|
|||||||
* @return The serializable object type
|
* @return The serializable object type
|
||||||
*/
|
*/
|
||||||
Serialize::Type *GetSerializableType() const { return this->s_type; }
|
Serialize::Type *GetSerializableType() const { return this->s_type; }
|
||||||
|
const auto &GetSerializableName() const { return this->s_name; }
|
||||||
|
|
||||||
static const std::list<Serializable *> &GetItems();
|
static const std::list<Serializable *> &GetItems();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CoreExport Serialize::Data
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
/** The specified data types of known fields. */
|
||||||
|
std::map<Anope::string, Serialize::DataType> types;
|
||||||
|
|
||||||
|
Data() = default;
|
||||||
|
|
||||||
|
/** Internal method for loading data from the database.
|
||||||
|
* @param key The field to get the value of.
|
||||||
|
* @param value The location to store the retrieved value.
|
||||||
|
*/
|
||||||
|
virtual bool LoadInternal(const Anope::string &key, Anope::string &value) = 0;
|
||||||
|
|
||||||
|
/** Internal method for storing data in the database.
|
||||||
|
* @param key The field to set the value of.
|
||||||
|
* @param value The value of the field.
|
||||||
|
*/
|
||||||
|
virtual bool StoreInternal(const Anope::string &key, const Anope::string &value) = 0;
|
||||||
|
|
||||||
|
/** Sets the data type of the specified field. This is called automatically from \ref Store.
|
||||||
|
* @param key The field to specify the data type for.
|
||||||
|
* @param dt The data type of the field.
|
||||||
|
*/
|
||||||
|
void SetType(const Anope::string &key, Serialize::DataType dt);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~Data() = default;
|
||||||
|
|
||||||
|
/** Retrieves the data type for the specified field. If the field does not have a data type
|
||||||
|
* specified then it will default to TEXT.
|
||||||
|
* @param key The field to retrieve the data type for.
|
||||||
|
*/
|
||||||
|
Serialize::DataType GetType(const Anope::string &key) const;
|
||||||
|
|
||||||
|
/** Retrieves a unique hash for the data set. */
|
||||||
|
virtual size_t Hash() const { throw CoreException("Not supported"); }
|
||||||
|
|
||||||
|
/** Loads the value of a specific field.
|
||||||
|
* @param key The field to get the value of.
|
||||||
|
* @param def The default value if none is set.
|
||||||
|
*/
|
||||||
|
template <typename T = Anope::string>
|
||||||
|
T Load(const Anope::string &key, T def = T())
|
||||||
|
{
|
||||||
|
T out;
|
||||||
|
if (!TryLoad(key, out))
|
||||||
|
out = def;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stores the value of a specific field, automatically setting its type.
|
||||||
|
* @param key The field to set the value of.
|
||||||
|
* @param value The value of the field.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void Store(const Anope::string &key, const T &value)
|
||||||
|
{
|
||||||
|
using Type = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<Type, bool>)
|
||||||
|
SetType(key, DataType::BOOL);
|
||||||
|
else if constexpr (std::is_floating_point_v<Type>)
|
||||||
|
SetType(key, DataType::FLOAT);
|
||||||
|
else if constexpr (std::is_integral_v<Type> && std::is_signed_v<Type>)
|
||||||
|
SetType(key, DataType::INT);
|
||||||
|
else if constexpr (std::is_integral_v<Type> && std::is_unsigned_v<Type>)
|
||||||
|
SetType(key, DataType::UINT);
|
||||||
|
|
||||||
|
StoreInternal(key, Anope::ToString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tries to load the value of a specific field.
|
||||||
|
* @param key The field to get the value of.
|
||||||
|
* @param out The location to store the retrieved value.
|
||||||
|
*/
|
||||||
|
template <typename T = Anope::string>
|
||||||
|
bool TryLoad(const Anope::string &key, T &out)
|
||||||
|
{
|
||||||
|
Anope::string out_str;
|
||||||
|
if (!LoadInternal(key, out_str))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto out_opt = Anope::TryConvert<T>(out_str);
|
||||||
|
if (!out_opt)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
out = out_opt.value();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* A serializable type. There should be a single instance of a subclass of this
|
/* A serializable type. There should be a single instance of a subclass of this
|
||||||
* for each subclass of Serializable as this is what is used to serialize and
|
* for each subclass of Serializable as this is what is used to serialize and
|
||||||
* deserialize data from the database.
|
* deserialize data from the database.
|
||||||
|
|||||||
+4
-4
@@ -57,11 +57,11 @@ private:
|
|||||||
/* Uplink for this server */
|
/* Uplink for this server */
|
||||||
Server *uplink;
|
Server *uplink;
|
||||||
/* Server is syncing */
|
/* Server is syncing */
|
||||||
bool syncing;
|
bool syncing = true;
|
||||||
/* The server is juped */
|
/* The server is juped */
|
||||||
bool juped;
|
bool juped;
|
||||||
/* The server is about to quit */
|
/* The server is about to quit */
|
||||||
bool quitting;
|
bool quitting = false;
|
||||||
/* Reason this server was quit */
|
/* Reason this server was quit */
|
||||||
Anope::string quit_reason;
|
Anope::string quit_reason;
|
||||||
|
|
||||||
@@ -69,12 +69,12 @@ public:
|
|||||||
/** Constructor
|
/** Constructor
|
||||||
* @param uplink The uplink this server is from, is only NULL when creating Me
|
* @param uplink The uplink this server is from, is only NULL when creating Me
|
||||||
* @param name The server name
|
* @param name The server name
|
||||||
* @param hops Hops from services server
|
|
||||||
* @param description Server rdescription
|
* @param description Server rdescription
|
||||||
* @param sid Server sid/numeric
|
* @param sid Server sid/numeric
|
||||||
|
* @param hops Hops from services server
|
||||||
* @param jupe If the server is juped
|
* @param jupe If the server is juped
|
||||||
*/
|
*/
|
||||||
Server(Server *uplink, const Anope::string &name, unsigned hops, const Anope::string &description, const Anope::string &sid = "", bool jupe = false);
|
Server(Server *uplink, const Anope::string &name, const Anope::string &description, const Anope::string &sid = "", unsigned hops = 0, bool jupe = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Destructor
|
/** Destructor
|
||||||
|
|||||||
+2
-2
@@ -151,14 +151,14 @@ public:
|
|||||||
const auto &GetServiceType() const { return type; }
|
const auto &GetServiceType() const { return type; }
|
||||||
|
|
||||||
/** Invalidates the reference and changes the name of the referenced service. */
|
/** Invalidates the reference and changes the name of the referenced service. */
|
||||||
void SetServiceName(const Anope::string& newname)
|
void SetServiceName(const Anope::string &newname)
|
||||||
{
|
{
|
||||||
this->invalid = true;
|
this->invalid = true;
|
||||||
this->name = newname;
|
this->name = newname;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Invalidates the reference and changes the type of the referenced service. */
|
/** Invalidates the reference and changes the type of the referenced service. */
|
||||||
void SetServiceType(const Anope::string& newtype)
|
void SetServiceType(const Anope::string &newtype)
|
||||||
{
|
{
|
||||||
this->invalid = true;
|
this->invalid = true;
|
||||||
this->type = newtype;
|
this->type = newtype;
|
||||||
|
|||||||
+1
-3
@@ -27,7 +27,7 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <istream>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
@@ -41,8 +41,6 @@
|
|||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "sysconf.h"
|
#include "sysconf.h"
|
||||||
|
|
||||||
#define BUFSIZE 1024
|
|
||||||
|
|
||||||
#define _(x) x
|
#define _(x) x
|
||||||
#define N_(x, y) x, y
|
#define N_(x, y) x, y
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,6 @@
|
|||||||
# include <afunix.h>
|
# include <afunix.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define NET_BUFSIZE 65535
|
|
||||||
|
|
||||||
/** A sockaddr union used to combine IPv4 and IPv6 sockaddrs
|
/** A sockaddr union used to combine IPv4 and IPv6 sockaddrs
|
||||||
*/
|
*/
|
||||||
union CoreExport sockaddrs
|
union CoreExport sockaddrs
|
||||||
|
|||||||
@@ -79,6 +79,12 @@ namespace Anope
|
|||||||
*/
|
*/
|
||||||
extern CoreExport Anope::string FormatCTCP(const Anope::string &name, const Anope::string &body = "");
|
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.
|
/** Parses a CTCP message received from a client.
|
||||||
* @param text The raw message to parse.
|
* @param text The raw message to parse.
|
||||||
* @param name The location to store the name of the CTCP.
|
* @param name The location to store the name of the CTCP.
|
||||||
|
|||||||
+3
-14
@@ -31,23 +31,17 @@ private:
|
|||||||
*/
|
*/
|
||||||
time_t secs;
|
time_t secs;
|
||||||
|
|
||||||
/** True if this is a repeating timer
|
|
||||||
*/
|
|
||||||
bool repeat;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Constructor, initializes the triggering time
|
/** Constructor, initializes the triggering time
|
||||||
* @param time_from_now The number of seconds from now to trigger the timer
|
* @param time_from_now The number of seconds from now to trigger the timer
|
||||||
* @param repeating Repeat this timer every time_from_now if this is true
|
|
||||||
*/
|
*/
|
||||||
Timer(time_t time_from_now, bool repeating = false);
|
Timer(time_t time_from_now);
|
||||||
|
|
||||||
/** Constructor, initializes the triggering time
|
/** Constructor, initializes the triggering time
|
||||||
* @param creator The creator of the timer
|
* @param creator The creator of the timer
|
||||||
* @param time_from_now The number of seconds from now to trigger the timer
|
* @param time_from_now The number of seconds from now to trigger the timer
|
||||||
* @param repeating Repeat this timer every time_from_now if this is true
|
|
||||||
*/
|
*/
|
||||||
Timer(Module *creator, time_t time_from_now, bool repeating = false);
|
Timer(Module *creator, time_t time_from_now);
|
||||||
|
|
||||||
/** Destructor, removes the timer from the list
|
/** Destructor, removes the timer from the list
|
||||||
*/
|
*/
|
||||||
@@ -63,11 +57,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
time_t GetTimer() const;
|
time_t GetTimer() const;
|
||||||
|
|
||||||
/** Returns true if the timer is set to repeat
|
|
||||||
* @return Returns true if the timer is set to repeat
|
|
||||||
*/
|
|
||||||
bool GetRepeat() const;
|
|
||||||
|
|
||||||
/** Set the interval between ticks
|
/** Set the interval between ticks
|
||||||
* @paramt t The new interval
|
* @paramt t The new interval
|
||||||
*/
|
*/
|
||||||
@@ -86,7 +75,7 @@ public:
|
|||||||
/** Called when the timer ticks
|
/** Called when the timer ticks
|
||||||
* This should be overridden with something useful
|
* This should be overridden with something useful
|
||||||
*/
|
*/
|
||||||
virtual void Tick() = 0;
|
virtual bool Tick() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** This class manages sets of Timers, and triggers them at their defined times.
|
/** This class manages sets of Timers, and triggers them at their defined times.
|
||||||
|
|||||||
+3
-1
@@ -54,7 +54,9 @@ class UplinkSocket final
|
|||||||
, public BufferedSocket
|
, public BufferedSocket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool error;
|
bool error = false;
|
||||||
|
size_t recv_msgs = 0;
|
||||||
|
size_t sent_msgs = 0;
|
||||||
UplinkSocket();
|
UplinkSocket();
|
||||||
~UplinkSocket();
|
~UplinkSocket();
|
||||||
bool ProcessRead() override;
|
bool ProcessRead() override;
|
||||||
|
|||||||
+17
-2
@@ -18,6 +18,8 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "sysconf.h"
|
||||||
|
|
||||||
static std::string get_git_hash(const std::string &git_dir)
|
static std::string get_git_hash(const std::string &git_dir)
|
||||||
{
|
{
|
||||||
std::fstream fd;
|
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)
|
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";
|
std::string build = "#define BUILD 1";
|
||||||
|
|
||||||
|
#if !REPRODUCIBLE_BUILD
|
||||||
|
fd.open(buildh.c_str(), std::ios::in);
|
||||||
|
|
||||||
if (fd.is_open())
|
if (fd.is_open())
|
||||||
{
|
{
|
||||||
for (std::string filebuf; getline(fd, filebuf);)
|
for (std::string filebuf; getline(fd, filebuf);)
|
||||||
{
|
{
|
||||||
if (!filebuf.find("#define BUILD"))
|
if (!filebuf.find("#define BUILD\t"))
|
||||||
{
|
{
|
||||||
size_t tab = filebuf.find(' ');
|
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();
|
fd.clear();
|
||||||
|
#endif
|
||||||
|
|
||||||
fd.open(buildh.c_str(), std::ios::out);
|
fd.open(buildh.c_str(), std::ios::out);
|
||||||
if (!fd.is_open())
|
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
|
fd << "/* This file is automatically generated by version.cpp - do not edit it! */" << std::endl
|
||||||
<< "#pragma once" << std::endl
|
<< "#pragma once" << std::endl
|
||||||
<< build << 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())
|
if (!git_version.empty())
|
||||||
fd << "#define VERSION_GIT \"" << git_version << "\"" << std::endl;
|
fd << "#define VERSION_GIT \"" << git_version << "\"" << std::endl;
|
||||||
fd.close();
|
fd.close();
|
||||||
|
|||||||
+2146
-123
File diff suppressed because it is too large
Load Diff
+5530
-4106
File diff suppressed because it is too large
Load Diff
+2796
-586
File diff suppressed because it is too large
Load Diff
+5
-4
@@ -15,14 +15,15 @@ fi
|
|||||||
find ../ \
|
find ../ \
|
||||||
! -path '../docs/*' \
|
! -path '../docs/*' \
|
||||||
-a ! -path '../modules/third/*' \
|
-a ! -path '../modules/third/*' \
|
||||||
|
-a ! -path '../run/*' \
|
||||||
|
-a ! -path '../vendor/*' \
|
||||||
-a \( -name '*.cpp' \
|
-a \( -name '*.cpp' \
|
||||||
-o -name '*.h' \
|
-o -name '*.h' \
|
||||||
-o -name '*.conf' \
|
-o -name '*.conf' \
|
||||||
\) \
|
\) \
|
||||||
-exec \
|
-print0 | sort -z | xargs -0 -I {} \
|
||||||
xgettext \
|
xgettext \
|
||||||
--language=C++ \
|
--language=C++ \
|
||||||
--sort-output \
|
|
||||||
--default-domain=Anope \
|
--default-domain=Anope \
|
||||||
--join-existing \
|
--join-existing \
|
||||||
--output=anope.pot \
|
--output=anope.pot \
|
||||||
@@ -30,13 +31,13 @@ find ../ \
|
|||||||
--keyword \
|
--keyword \
|
||||||
--keyword=_ \
|
--keyword=_ \
|
||||||
--keyword=N_:1,2 \
|
--keyword=N_:1,2 \
|
||||||
{} +
|
{}
|
||||||
|
|
||||||
for f in *.po
|
for f in *.po
|
||||||
do
|
do
|
||||||
echo "Merging $f"
|
echo "Merging $f"
|
||||||
msgmerge \
|
msgmerge \
|
||||||
--no-location \
|
--add-location=file \
|
||||||
--no-wrap \
|
--no-wrap \
|
||||||
--sort-output \
|
--sort-output \
|
||||||
--update \
|
--update \
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ macro(inline_cmake TARGET FILE)
|
|||||||
set(CODE "${CODE}\n${CLEAN_LINE}")
|
set(CODE "${CODE}\n${CLEAN_LINE}")
|
||||||
endif()
|
endif()
|
||||||
elseif(LINE MATCHES "^/// BEGIN CMAKE$")
|
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)
|
set(IN_CODE ON)
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
@@ -77,6 +77,12 @@ macro(build_module SRC MODULE_SRC)
|
|||||||
PREFIX ""
|
PREFIX ""
|
||||||
SUFFIX ""
|
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})
|
add_dependencies(${SO} ${PROGRAM_NAME})
|
||||||
if(HAVE_LOCALIZATION)
|
if(HAVE_LOCALIZATION)
|
||||||
add_dependencies(${SO} module_language)
|
add_dependencies(${SO} module_language)
|
||||||
|
|||||||
@@ -84,6 +84,13 @@ public:
|
|||||||
"can then configure the bot for the channel so it fits "
|
"can then configure the bot for the channel so it fits "
|
||||||
"your needs."
|
"your needs."
|
||||||
));
|
));
|
||||||
|
|
||||||
|
ExampleWrapper()
|
||||||
|
.AddEntry("#opers OperServ", _(
|
||||||
|
"Assigns the \037OperServ\037 bot to \037#opers\037."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -150,6 +157,13 @@ public:
|
|||||||
"be able to reassign a bot later without having to reconfigure "
|
"be able to reassign a bot later without having to reconfigure "
|
||||||
"it entirely."
|
"it entirely."
|
||||||
));
|
));
|
||||||
|
|
||||||
|
ExampleWrapper()
|
||||||
|
.AddEntry("#opers", _(
|
||||||
|
"Unassigns a previously assigned bot from \037#opers\037."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ struct BadWordsImpl final
|
|||||||
|
|
||||||
BadWordsImpl::~BadWordsImpl()
|
BadWordsImpl::~BadWordsImpl()
|
||||||
{
|
{
|
||||||
for (list::iterator it = badwords->begin(); it != badwords->end();)
|
for (auto it = badwords->begin(); it != badwords->end();)
|
||||||
{
|
{
|
||||||
auto *bw = *it;
|
auto *bw = *it;
|
||||||
++it;
|
++it;
|
||||||
@@ -160,10 +160,10 @@ BadWordImpl::~BadWordImpl()
|
|||||||
ChannelInfo *ci = ChannelInfo::Find(chan);
|
ChannelInfo *ci = ChannelInfo::Find(chan);
|
||||||
if (ci)
|
if (ci)
|
||||||
{
|
{
|
||||||
BadWordsImpl *badwords = ci->GetExt<BadWordsImpl>(BOTSERV_BAD_WORDS_EXT);
|
auto *badwords = ci->GetExt<BadWordsImpl>(BOTSERV_BAD_WORDS_EXT);
|
||||||
if (badwords)
|
if (badwords)
|
||||||
{
|
{
|
||||||
BadWordsImpl::list::iterator it = std::find(badwords->badwords->begin(), badwords->badwords->end(), this);
|
auto it = std::find(badwords->badwords->begin(), badwords->badwords->end(), this);
|
||||||
if (it != badwords->badwords->end())
|
if (it != badwords->badwords->end())
|
||||||
badwords->badwords->erase(it);
|
badwords->badwords->erase(it);
|
||||||
}
|
}
|
||||||
@@ -172,28 +172,20 @@ BadWordImpl::~BadWordImpl()
|
|||||||
|
|
||||||
Serializable *BadWordTypeImpl::Unserialize(Serializable *obj, Serialize::Data &data) const
|
Serializable *BadWordTypeImpl::Unserialize(Serializable *obj, Serialize::Data &data) const
|
||||||
{
|
{
|
||||||
Anope::string sci, sword;
|
auto *ci = ChannelInfo::Find(data.Load("ci"));
|
||||||
|
|
||||||
data["ci"] >> sci;
|
|
||||||
data["word"] >> sword;
|
|
||||||
|
|
||||||
ChannelInfo *ci = ChannelInfo::Find(sci);
|
|
||||||
if (!ci)
|
if (!ci)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
Anope::string n;
|
|
||||||
data["type"] >> n;
|
|
||||||
|
|
||||||
BadWordImpl *bw;
|
BadWordImpl *bw;
|
||||||
if (obj)
|
if (obj)
|
||||||
bw = anope_dynamic_static_cast<BadWordImpl *>(obj);
|
bw = anope_dynamic_static_cast<BadWordImpl *>(obj);
|
||||||
else
|
else
|
||||||
bw = new BadWordImpl();
|
bw = new BadWordImpl();
|
||||||
bw->chan = sci;
|
bw->chan = ci->name;
|
||||||
bw->word = sword;
|
bw->word = data.Load("word");
|
||||||
bw->type = StringToType(n);
|
bw->type = StringToType(data.Load("type"));
|
||||||
|
|
||||||
BadWordsImpl *bws = ci->Require<BadWordsImpl>(BOTSERV_BAD_WORDS_EXT);
|
auto *bws = ci->Require<BadWordsImpl>(BOTSERV_BAD_WORDS_EXT);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
bws->badwords->push_back(bw);
|
bws->badwords->push_back(bw);
|
||||||
|
|
||||||
@@ -337,7 +329,7 @@ private:
|
|||||||
realword = word.substr(0, pos);
|
realword = word.substr(0, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned badwordsmax = Config->GetModule(this->module).Get<unsigned>("badwordsmax");
|
auto badwordsmax = Config->GetModule(this->module).Get<unsigned>("badwordsmax");
|
||||||
if (badwords->GetBadWordCount() >= badwordsmax)
|
if (badwords->GetBadWordCount() >= badwordsmax)
|
||||||
{
|
{
|
||||||
source.Reply(_("You can only have %d bad words entries on a channel."), badwordsmax);
|
source.Reply(_("You can only have %d bad words entries on a channel."), badwordsmax);
|
||||||
@@ -496,7 +488,7 @@ public:
|
|||||||
"\n\n"
|
"\n\n"
|
||||||
"The \002DEL\002 command removes the given word from the "
|
"The \002DEL\002 command removes the given word from the "
|
||||||
"bad words list. If a list of entry numbers is given, those "
|
"bad words list. If a list of entry numbers is given, those "
|
||||||
"entries are deleted. (See the example for LIST below.)"
|
"entries are deleted."
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"The \002LIST\002 command displays the bad words list. If "
|
"The \002LIST\002 command displays the bad words list. If "
|
||||||
"a wildcard mask is given, only those entries matching the "
|
"a wildcard mask is given, only those entries matching the "
|
||||||
@@ -509,14 +501,44 @@ public:
|
|||||||
source.service->GetQueryCommand("generic/help").c_str(),
|
source.service->GetQueryCommand("generic/help").c_str(),
|
||||||
source.command.nobreak().c_str());
|
source.command.nobreak().c_str());
|
||||||
|
|
||||||
ExampleWrapper examples;
|
ExampleWrapper()
|
||||||
examples.AddEntry("#channel LIST 2-5,7-9", _(
|
.AddEntry("#channel ADD smeg", _(
|
||||||
"Lists bad word entries on \037#channel\037 numbered 2 through 5 and 7 through 9."
|
"Add \035smeg\035 to the bad word list of \035#channel\035. If a user says this "
|
||||||
));
|
"word anywhere in a message they will be kicked."
|
||||||
examples.AddEntry("#channel LIST *UwU*", _(
|
))
|
||||||
"Lists bad word entries on \037#channel\037 that match \037*UwU*\037."
|
.AddEntry("#channel ADD smeg SINGLE", _(
|
||||||
));
|
"Add \035smeg\035 to the bad word list of \035#channel\035. If a user says only "
|
||||||
examples.SendTo(source);
|
"this word in a message they will be kicked."
|
||||||
|
))
|
||||||
|
.AddEntry("#channel ADD smeg START", _(
|
||||||
|
"Add \035smeg\035 to the bad word list of \035#channel\035. If a user says this "
|
||||||
|
"word at the start of a message they will be kicked."
|
||||||
|
))
|
||||||
|
.AddEntry("#channel ADD smeg END", _(
|
||||||
|
"Add \035smeg\035 to the bad word list of \035#channel\035. If a user says this "
|
||||||
|
"word at the start of a message they will be kicked."
|
||||||
|
))
|
||||||
|
.AddEntry("#channel CLEAR", _(
|
||||||
|
"Clears all bad word entries set on \035#channel\035."
|
||||||
|
))
|
||||||
|
.AddEntry("#channel DEL 2-5,7-9", _(
|
||||||
|
"Deletes bad word entries set on \035#channel\035 numbered 2 through 5 and 7 "
|
||||||
|
"through 9."
|
||||||
|
))
|
||||||
|
.AddEntry("#channel DEL heck", _(
|
||||||
|
"Deletes \035heck\035 from the bad word list of \035#channel\035."
|
||||||
|
))
|
||||||
|
.AddEntry("#channel LIST", _(
|
||||||
|
"Lists all bad word entries set on \035#channel\035."
|
||||||
|
))
|
||||||
|
.AddEntry("#channel LIST 2-5,7-9", _(
|
||||||
|
"Lists bad word entries set on \035#channel\035 numbered 2 through 5 and 7 through "
|
||||||
|
"9."
|
||||||
|
))
|
||||||
|
.AddEntry("#channel LIST *frack*", _(
|
||||||
|
"Lists bad word entries set on \035#channel\035 that match \035*frack*\035."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -351,8 +351,7 @@ public:
|
|||||||
"channels."
|
"channels."
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"\002%s\033ADD\002 adds a bot with the given nickname, username, "
|
"\002%s\033ADD\002 adds a bot with the given nickname, username, "
|
||||||
"hostname and realname. Since no integrity checks are done "
|
"hostname and realname."
|
||||||
"for these settings, be really careful."
|
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"\002%s\033CHANGE\002 allows you to change the nickname, username, hostname "
|
"\002%s\033CHANGE\002 allows you to change the nickname, username, hostname "
|
||||||
"or realname of a bot without deleting it (and "
|
"or realname of a bot without deleting it (and "
|
||||||
@@ -367,6 +366,25 @@ public:
|
|||||||
source.command.nobreak().c_str(),
|
source.command.nobreak().c_str(),
|
||||||
source.command.nobreak().c_str(),
|
source.command.nobreak().c_str(),
|
||||||
source.command.nobreak().c_str());
|
source.command.nobreak().c_str());
|
||||||
|
|
||||||
|
ExampleWrapper()
|
||||||
|
.AddEntry("ADD Chii chobit persocom.test Chii Motosuwa", _(
|
||||||
|
"Adds a new bot with the nickname \035Chii\035, username \035chobit\035, hostname "
|
||||||
|
"\035persocom.test\035, and realname \035Chii Motosuwa\035."
|
||||||
|
))
|
||||||
|
.AddEntry("CHANGE Elda Chii", _(
|
||||||
|
"Renames an existing bot called \035Elda\035 to \035Chii\035."
|
||||||
|
))
|
||||||
|
.AddEntry("CHANGE Chii Mahoro saint vesper.test Mahoro Andou", _(
|
||||||
|
"Changes all of the information of the bot called \035Chii\035. The new nickname "
|
||||||
|
"will be \035Mahoro\035, the new username will be \035saint\035, the new hostname "
|
||||||
|
"will be \035vesper.test\035, and the new realname will be \035Mahoro Andou\035."
|
||||||
|
))
|
||||||
|
.AddEntry("DEL Mahoro", _(
|
||||||
|
"Deletes a bot called \035Mahoro\035."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -71,6 +71,13 @@ public:
|
|||||||
this->SendSyntax(source);
|
this->SendSyntax(source);
|
||||||
source.Reply(" ");
|
source.Reply(" ");
|
||||||
source.Reply(_("Makes the bot say the specified text on the specified channel."));
|
source.Reply(_("Makes the bot say the specified text on the specified channel."));
|
||||||
|
|
||||||
|
ExampleWrapper()
|
||||||
|
.AddEntry(_("#chat hello all"), _(
|
||||||
|
"Sends a message to \035#chat\035 saying \035hello all\035."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -126,9 +133,16 @@ public:
|
|||||||
this->SendSyntax(source);
|
this->SendSyntax(source);
|
||||||
source.Reply(" ");
|
source.Reply(" ");
|
||||||
source.Reply(_(
|
source.Reply(_(
|
||||||
"Makes the bot do the equivalent of a \"/me\" command "
|
"Makes the bot do the equivalent of a \"/me\" command on the specified channel using "
|
||||||
"on the specified channel using the specified text."
|
"the specified text."
|
||||||
));
|
));
|
||||||
|
|
||||||
|
ExampleWrapper()
|
||||||
|
.AddEntry(_("#chat is eating pizza"), _(
|
||||||
|
"Sends an action message to \035#chat\035 saying \035is eating pizza\035."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public:
|
|||||||
info[_("Real name")] = bi->realname;
|
info[_("Real name")] = bi->realname;
|
||||||
info[_("Created")] = Anope::strftime(bi->created, source.GetAccount());
|
info[_("Created")] = Anope::strftime(bi->created, source.GetAccount());
|
||||||
info[_("Options")] = bi->oper_only ? _("Private") : _("None");
|
info[_("Options")] = bi->oper_only ? _("Private") : _("None");
|
||||||
info[_("Used on")] = Anope::Format(Language::Translate(source.nc, bi->GetChannelCount(), N_("%u channel", "%u channels")), bi->GetChannelCount());
|
info[_("Used on")] = Anope::Format(source.Translate(bi->GetChannelCount(), N_("%u channel", "%u channels")), bi->GetChannelCount());
|
||||||
|
|
||||||
FOREACH_MOD(OnBotInfo, (source, bi, ci, info));
|
FOREACH_MOD(OnBotInfo, (source, bi, ci, info));
|
||||||
info.SendTo(source);
|
info.SendTo(source);
|
||||||
@@ -83,8 +83,8 @@ public:
|
|||||||
source.Reply(CHAN_INFO_HEADER, ci->name.c_str());
|
source.Reply(CHAN_INFO_HEADER, ci->name.c_str());
|
||||||
info[_("Bot nick")] = ci->bi ? ci->bi->nick : _("not assigned yet");
|
info[_("Bot nick")] = ci->bi ? ci->bi->nick : _("not assigned yet");
|
||||||
|
|
||||||
Anope::string enabled = Language::Translate(source.nc, _("Enabled"));
|
Anope::string enabled = source.Translate(_("Enabled"));
|
||||||
Anope::string disabled = Language::Translate(source.nc, _("Disabled"));
|
Anope::string disabled = source.Translate(_("Disabled"));
|
||||||
|
|
||||||
FOREACH_MOD(OnBotInfo, (source, bi, ci, info));
|
FOREACH_MOD(OnBotInfo, (source, bi, ci, info));
|
||||||
info.SendTo(source);
|
info.SendTo(source);
|
||||||
@@ -104,12 +104,23 @@ public:
|
|||||||
"you'll get information about a bot, such as creation "
|
"you'll get information about a bot, such as creation "
|
||||||
"time or number of channels it is on."
|
"time or number of channels it is on."
|
||||||
), source.service->nick.c_str());
|
), source.service->nick.c_str());
|
||||||
|
|
||||||
|
ExampleWrapper()
|
||||||
|
.AddEntry("#example", _(
|
||||||
|
"Shows information about the bot assigned to \035#example\035 and its kickers and "
|
||||||
|
"options."
|
||||||
|
))
|
||||||
|
.AddEntry("ChanServ", _(
|
||||||
|
"Shows information about the \035ChanServ\035 bot."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Anope::string GetDesc(CommandSource &source) const override
|
Anope::string GetDesc(CommandSource &source) const override
|
||||||
{
|
{
|
||||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Allows you to see %s information about a channel or a bot")), source.service->nick.c_str());
|
return Anope::Format(source.Translate(_("Allows you to see %s information about a channel or a bot")), source.service->nick.c_str());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+33
-30
@@ -51,7 +51,7 @@ struct KickerDataImpl final
|
|||||||
if (s->GetSerializableType()->GetName() != CHANNELINFO_TYPE)
|
if (s->GetSerializableType()->GetName() != CHANNELINFO_TYPE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ChannelInfo *ci = anope_dynamic_static_cast<const ChannelInfo *>(e);
|
const auto *ci = anope_dynamic_static_cast<const ChannelInfo *>(e);
|
||||||
auto *kd = this->Get(ci);
|
auto *kd = this->Get(ci);
|
||||||
if (kd == NULL)
|
if (kd == NULL)
|
||||||
return;
|
return;
|
||||||
@@ -85,33 +85,35 @@ struct KickerDataImpl final
|
|||||||
if (s->GetSerializableType()->GetName() != CHANNELINFO_TYPE)
|
if (s->GetSerializableType()->GetName() != CHANNELINFO_TYPE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChannelInfo *ci = anope_dynamic_static_cast<ChannelInfo *>(e);
|
auto *ci = anope_dynamic_static_cast<ChannelInfo *>(e);
|
||||||
auto *kd = ci->Require<BotServ::KickerData>(BOTSERV_KICKER_DATA_EXT);
|
auto *kd = ci->Require<BotServ::KickerData>(BOTSERV_KICKER_DATA_EXT);
|
||||||
|
|
||||||
data["kickerdata:amsgs"] >> kd->amsgs;
|
kd->amsgs = data.Load<bool>("kickerdata:amsgs");
|
||||||
data["kickerdata:badwords"] >> kd->badwords;
|
kd->badwords = data.Load<bool>("kickerdata:badwords");
|
||||||
data["kickerdata:bolds"] >> kd->bolds;
|
kd->bolds = data.Load<bool>("kickerdata:bolds");
|
||||||
data["kickerdata:caps"] >> kd->caps;
|
kd->caps = data.Load<bool>("kickerdata:caps");
|
||||||
data["kickerdata:colors"] >> kd->colors;
|
kd->colors = data.Load<bool>("kickerdata:colors");
|
||||||
data["kickerdata:flood"] >> kd->flood;
|
kd->flood = data.Load<bool>("kickerdata:flood");
|
||||||
data["kickerdata:italics"] >> kd->italics;
|
kd->italics = data.Load<bool>("kickerdata:italics");
|
||||||
data["kickerdata:repeat"] >> kd->repeat;
|
kd->repeat = data.Load<bool>("kickerdata:repeat");
|
||||||
data["kickerdata:reverses"] >> kd->reverses;
|
kd->reverses = data.Load<bool>("kickerdata:reverses");
|
||||||
data["kickerdata:underlines"] >> kd->underlines;
|
kd->underlines = data.Load<bool>("kickerdata:underlines");
|
||||||
|
|
||||||
data["capsmin"] >> kd->capsmin;
|
kd->capsmin = data.Load<int16_t>("capsmin");
|
||||||
data["capspercent"] >> kd->capspercent;
|
kd->capspercent = data.Load<int16_t>("capspercent");
|
||||||
data["floodlines"] >> kd->floodlines;
|
kd->floodlines = data.Load<int16_t>("floodlines");
|
||||||
data["floodsecs"] >> kd->floodsecs;
|
kd->floodsecs = data.Load<int16_t>("floodsecs");
|
||||||
data["repeattimes"] >> kd->repeattimes;
|
kd->repeattimes = data.Load<int16_t>("repeattimes");
|
||||||
data["dontkickops"] >> kd->dontkickops;
|
kd->dontkickops = data.Load<bool>("dontkickops");
|
||||||
data["dontkickvoices"] >> kd->dontkickvoices;
|
kd->dontkickvoices = data.Load<bool>("dontkickvoices");
|
||||||
|
|
||||||
Anope::string ttb, tok;
|
spacesepstream sep(data.Load("ttb"));
|
||||||
data["ttb"] >> ttb;
|
for (size_t i = 0; i < BotServ::TTB_SIZE; ++i)
|
||||||
spacesepstream sep(ttb);
|
|
||||||
for (int i = 0; sep.GetToken(tok) && i < BotServ::TTB_SIZE; ++i)
|
|
||||||
{
|
{
|
||||||
|
Anope::string tok;
|
||||||
|
if (!sep.GetToken(tok))
|
||||||
|
break;
|
||||||
|
|
||||||
if (auto n = Anope::TryConvert<int16_t>(tok))
|
if (auto n = Anope::TryConvert<int16_t>(tok))
|
||||||
kd->ttb[i] = n.value();
|
kd->ttb[i] = n.value();
|
||||||
}
|
}
|
||||||
@@ -988,7 +990,7 @@ public:
|
|||||||
void purge()
|
void purge()
|
||||||
{
|
{
|
||||||
time_t keepdata = Config->GetModule(me).Get<time_t>("keepdata");
|
time_t keepdata = Config->GetModule(me).Get<time_t>("keepdata");
|
||||||
for (data_type::iterator it = data_map.begin(), it_end = data_map.end(); it != it_end;)
|
for (auto it = data_map.begin(), it_end = data_map.end(); it != it_end;)
|
||||||
{
|
{
|
||||||
const Anope::string &user = it->first;
|
const Anope::string &user = it->first;
|
||||||
Data &bd = it->second;
|
Data &bd = it->second;
|
||||||
@@ -1028,17 +1030,17 @@ class BanDataPurger final
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BanDataPurger(Module *o)
|
BanDataPurger(Module *o)
|
||||||
: Timer(o, 300, true)
|
: Timer(o, 300)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tick() override
|
bool Tick() override
|
||||||
{
|
{
|
||||||
Log(LOG_DEBUG) << "bs_main: Running bandata purger";
|
Log(LOG_DEBUG) << "bs_main: Running bandata purger";
|
||||||
|
|
||||||
for (auto &[_, c] : ChannelList)
|
for (auto &[_, c] : ChannelList)
|
||||||
{
|
{
|
||||||
BanData *bd = c->GetExt<BanData>("bandata");
|
auto *bd = c->GetExt<BanData>("bandata");
|
||||||
if (bd != NULL)
|
if (bd != NULL)
|
||||||
{
|
{
|
||||||
bd->purge();
|
bd->purge();
|
||||||
@@ -1046,6 +1048,7 @@ public:
|
|||||||
c->Shrink<BanData>("bandata");
|
c->Shrink<BanData>("bandata");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1149,8 +1152,8 @@ public:
|
|||||||
if (!ci)
|
if (!ci)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Anope::string enabled = Language::Translate(source.nc, _("Enabled"));
|
Anope::string enabled = source.Translate(_("Enabled"));
|
||||||
Anope::string disabled = Language::Translate(source.nc, _("Disabled"));
|
Anope::string disabled = source.Translate(_("Disabled"));
|
||||||
auto *kd = kickerdata.Get(ci);
|
auto *kd = kickerdata.Get(ci);
|
||||||
|
|
||||||
if (kd && kd->badwords)
|
if (kd && kd->badwords)
|
||||||
@@ -1488,7 +1491,7 @@ public:
|
|||||||
|
|
||||||
if (ud->lastline.equals_ci(realbuf) && !ud->lasttarget.empty() && !ud->lasttarget.equals_ci(ci->name))
|
if (ud->lastline.equals_ci(realbuf) && !ud->lasttarget.empty() && !ud->lasttarget.equals_ci(ci->name))
|
||||||
{
|
{
|
||||||
for (User::ChanUserList::iterator it = u->chans.begin(); it != u->chans.end();)
|
for (auto it = u->chans.begin(); it != u->chans.end();)
|
||||||
{
|
{
|
||||||
Channel *chan = it->second->chan;
|
Channel *chan = it->second->chan;
|
||||||
++it;
|
++it;
|
||||||
|
|||||||
@@ -92,11 +92,12 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tick() override
|
bool Tick() override
|
||||||
{
|
{
|
||||||
Channel *c = Channel::Find(chname);
|
Channel *c = Channel::Find(chname);
|
||||||
if (c)
|
if (c)
|
||||||
c->RemoveMode(NULL, "BAN", mask);
|
c->RemoveMode(NULL, "BAN", mask);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -166,6 +167,13 @@ public:
|
|||||||
"be removed after the given time. Set to 0 to disable bans from "
|
"be removed after the given time. Set to 0 to disable bans from "
|
||||||
"automatically expiring."
|
"automatically expiring."
|
||||||
));
|
));
|
||||||
|
|
||||||
|
ExampleWrapper()
|
||||||
|
.AddEntry("#wibble 15m", _(
|
||||||
|
"Sets bot bans on \035#wibble\035 to expire after \03515 minutes\035."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,10 +82,10 @@ public:
|
|||||||
/** Called when the delay is up
|
/** Called when the delay is up
|
||||||
* @param The current time
|
* @param The current time
|
||||||
*/
|
*/
|
||||||
void Tick() override
|
bool Tick() override
|
||||||
{
|
{
|
||||||
if (!c)
|
if (!c)
|
||||||
return;
|
return false; // Dead channel.
|
||||||
|
|
||||||
/* In the event we don't part */
|
/* In the event we don't part */
|
||||||
c->RemoveMode(NULL, "SECRET");
|
c->RemoveMode(NULL, "SECRET");
|
||||||
@@ -101,6 +101,8 @@ public:
|
|||||||
/* If someone has rejoined this channel in the meantime, don't part the bot */
|
/* If someone has rejoined this channel in the meantime, don't part the bot */
|
||||||
else if (c->users.size() <= 1)
|
else if (c->users.size() <= 1)
|
||||||
c->ci->bi->Part(c);
|
c->ci->bi->Part(c);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -357,7 +359,7 @@ public:
|
|||||||
if (!chanserv_expire || Anope::NoExpire || Anope::ReadOnly)
|
if (!chanserv_expire || Anope::NoExpire || Anope::ReadOnly)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (registered_channel_map::const_iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; )
|
for (auto it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; )
|
||||||
{
|
{
|
||||||
ChannelInfo *ci = it->second;
|
ChannelInfo *ci = it->second;
|
||||||
++it;
|
++it;
|
||||||
@@ -369,7 +371,7 @@ public:
|
|||||||
if (ci->c)
|
if (ci->c)
|
||||||
{
|
{
|
||||||
time_t last_used = ci->last_used;
|
time_t last_used = ci->last_used;
|
||||||
for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end && last_used == ci->last_used; ++cit)
|
for (auto cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end && last_used == ci->last_used; ++cit)
|
||||||
ci->AccessFor(cit->second->user);
|
ci->AccessFor(cit->second->user);
|
||||||
expire = last_used == ci->last_used;
|
expire = last_used == ci->last_used;
|
||||||
}
|
}
|
||||||
@@ -397,11 +399,10 @@ public:
|
|||||||
return EVENT_CONTINUE;
|
return EVENT_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnUplinkSync(Server* s) override
|
void OnUplinkSync(Server *s) override
|
||||||
{
|
{
|
||||||
// We need to do this when the uplink is synced as we may not know if
|
// We need to do this when the uplink is synced as we may not know if
|
||||||
// the mode exists before then on some IRCds (e.g. InspIRCd).
|
// the mode exists before then on some IRCds (e.g. InspIRCd).
|
||||||
|
|
||||||
if (!persist)
|
if (!persist)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,17 @@ static inline void reset_levels(ChannelInfo *ci)
|
|||||||
ci->SetLevel(priv, level);
|
ci->SetLevel(priv, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Anope::string LevelToString(CommandSource &source, int16_t level)
|
||||||
|
{
|
||||||
|
if (level == ACCESS_INVALID)
|
||||||
|
return source.Translate(_("(disabled)"));
|
||||||
|
|
||||||
|
if (level == ACCESS_FOUNDER)
|
||||||
|
return source.Translate(_("(founder only)"));
|
||||||
|
|
||||||
|
return Anope::ToString(level);
|
||||||
|
}
|
||||||
|
|
||||||
class AccessChanAccess final
|
class AccessChanAccess final
|
||||||
: public ChanAccess
|
: public ChanAccess
|
||||||
{
|
{
|
||||||
@@ -81,6 +92,13 @@ public:
|
|||||||
{
|
{
|
||||||
return new AccessChanAccess(this);
|
return new AccessChanAccess(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAccess(CommandSource& source, const Privilege *p, Anope::map<Anope::string> &access) override
|
||||||
|
{
|
||||||
|
auto it = defaultLevels.find(p->name);
|
||||||
|
if (it != defaultLevels.end())
|
||||||
|
access[_("Level")] = LevelToString(source, it->second);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
AccessAccessProvider *AccessAccessProvider::me;
|
AccessAccessProvider *AccessAccessProvider::me;
|
||||||
|
|
||||||
@@ -276,7 +294,7 @@ private:
|
|||||||
ServiceReference<AccessProvider> provider("AccessProvider", "access/access");
|
ServiceReference<AccessProvider> provider("AccessProvider", "access/access");
|
||||||
if (!provider)
|
if (!provider)
|
||||||
return;
|
return;
|
||||||
AccessChanAccess *access = anope_dynamic_static_cast<AccessChanAccess *>(provider->Create());
|
auto *access = anope_dynamic_static_cast<AccessChanAccess *>(provider->Create());
|
||||||
access->SetMask(mask, ci);
|
access->SetMask(mask, ci);
|
||||||
access->creator = source.GetNick();
|
access->creator = source.GetNick();
|
||||||
access->level = level;
|
access->level = level;
|
||||||
@@ -285,7 +303,7 @@ private:
|
|||||||
access->description = description;
|
access->description = description;
|
||||||
ci->AddAccess(access);
|
ci->AddAccess(access);
|
||||||
|
|
||||||
FOREACH_MOD(OnAccessAdd, (ci, source, access));
|
FOREACH_MOD(OnAccessAdd, (ci, source, access, false));
|
||||||
|
|
||||||
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask << " with level " << level;
|
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask << " with level " << level;
|
||||||
if (p != NULL)
|
if (p != NULL)
|
||||||
@@ -376,7 +394,7 @@ private:
|
|||||||
|
|
||||||
ci->EraseAccess(Number - 1);
|
ci->EraseAccess(Number - 1);
|
||||||
|
|
||||||
FOREACH_MOD(OnAccessDel, (ci, source, access));
|
FOREACH_MOD(OnAccessDel, (ci, source, access, false));
|
||||||
delete access;
|
delete access;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,7 +420,7 @@ private:
|
|||||||
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << access->Mask();
|
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << access->Mask();
|
||||||
|
|
||||||
ci->EraseAccess(i - 1);
|
ci->EraseAccess(i - 1);
|
||||||
FOREACH_MOD(OnAccessDel, (ci, source, access));
|
FOREACH_MOD(OnAccessDel, (ci, source, access, false));
|
||||||
delete access;
|
delete access;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -416,6 +434,8 @@ private:
|
|||||||
void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, ListFormatter &list)
|
void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, ListFormatter &list)
|
||||||
{
|
{
|
||||||
const Anope::string &nick = params.size() > 2 ? params[2] : "";
|
const Anope::string &nick = params.size() > 2 ? params[2] : "";
|
||||||
|
const auto show_all = params.size() > 3 && params[3].equals_ci("ALL");
|
||||||
|
unsigned foreign = 0;
|
||||||
|
|
||||||
if (!ci->GetAccessCount())
|
if (!ci->GetAccessCount())
|
||||||
source.Reply(_("%s access list is empty."), ci->name.c_str());
|
source.Reply(_("%s access list is empty."), ci->name.c_str());
|
||||||
@@ -426,9 +446,16 @@ private:
|
|||||||
{
|
{
|
||||||
ListFormatter &list;
|
ListFormatter &list;
|
||||||
ChannelInfo *ci;
|
ChannelInfo *ci;
|
||||||
|
bool show_all;
|
||||||
|
unsigned &foreign;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AccessListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci)
|
AccessListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist, bool _show_all, unsigned &_foreign)
|
||||||
|
: NumberList(numlist, false)
|
||||||
|
, list(_list)
|
||||||
|
, ci(_ci)
|
||||||
|
, show_all(_show_all)
|
||||||
|
, foreign(_foreign)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,11 +465,16 @@ private:
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const ChanAccess *access = ci->GetAccess(number - 1);
|
const ChanAccess *access = ci->GetAccess(number - 1);
|
||||||
|
if (!show_all && access->provider->name != "access/access")
|
||||||
|
{
|
||||||
|
foreign++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AddEntry(this->list, ci, access, number);
|
AddEntry(this->list, ci, access, number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nl_list(list, ci, nick);
|
nl_list(list, ci, nick, show_all, foreign);
|
||||||
nl_list.Process();
|
nl_list.Process();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -454,6 +486,12 @@ private:
|
|||||||
if (!nick.empty() && !Anope::Match(access->Mask(), nick))
|
if (!nick.empty() && !Anope::Match(access->Mask(), nick))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!show_all && access->provider->name != "access/access")
|
||||||
|
{
|
||||||
|
foreign++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
AddEntry(list, ci, access, i + 1);
|
AddEntry(list, ci, access, i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -466,6 +504,14 @@ private:
|
|||||||
list.SendTo(source);
|
list.SendTo(source);
|
||||||
source.Reply(_("End of access list"));
|
source.Reply(_("End of access list"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (foreign)
|
||||||
|
{
|
||||||
|
const auto full_command = Anope::Format("%s %s %s %s", source.command.c_str(),
|
||||||
|
ci->name.c_str(), params[1].upper().c_str(), nick.empty() ? "*" : nick.c_str()).nobreak();
|
||||||
|
|
||||||
|
source.Reply(foreign, CHAN_ACCESS_FOREIGN, foreign, full_command.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
|
void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
|
||||||
@@ -531,8 +577,8 @@ public:
|
|||||||
this->SetDesc(_("Modify the list of privileged users"));
|
this->SetDesc(_("Modify the list of privileged users"));
|
||||||
this->SetSyntax(_("\037channel\037 ADD \037mask\037 \037level\037 [\037description\037]"));
|
this->SetSyntax(_("\037channel\037 ADD \037mask\037 \037level\037 [\037description\037]"));
|
||||||
this->SetSyntax(_("\037channel\037 DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
|
this->SetSyntax(_("\037channel\037 DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
|
||||||
this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
|
this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037] [ALL]"));
|
||||||
this->SetSyntax(_("\037channel\037 VIEW [\037mask\037 | \037list\037]"));
|
this->SetSyntax(_("\037channel\037 VIEW [\037mask\037 | \037list\037] [ALL]"));
|
||||||
this->SetSyntax(_("\037channel\037 CLEAR"));
|
this->SetSyntax(_("\037channel\037 CLEAR"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,7 +681,8 @@ public:
|
|||||||
"The \002%s\033LIST\002 command displays the access list. If "
|
"The \002%s\033LIST\002 command displays the access list. If "
|
||||||
"a wildcard mask is given, only those entries matching the "
|
"a wildcard mask is given, only those entries matching the "
|
||||||
"mask are displayed. If a list of entry numbers is given, "
|
"mask are displayed. If a list of entry numbers is given, "
|
||||||
"only those entries are shown."
|
"only those entries are shown. The \002ALL\002 option allows "
|
||||||
|
"listing entries from other access systems as well as levels."
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"The \002%s\033VIEW\002 command displays the access list similar "
|
"The \002%s\033VIEW\002 command displays the access list similar "
|
||||||
"to \002%s\033LIST\002 but shows the creator and last used time."
|
"to \002%s\033LIST\002 but shows the creator and last used time."
|
||||||
@@ -660,7 +707,7 @@ public:
|
|||||||
|
|
||||||
BotInfo *bi;
|
BotInfo *bi;
|
||||||
Anope::string cmd;
|
Anope::string cmd;
|
||||||
if (Command::FindCommandFromService("chanserv/levels", bi, cmd))
|
if (Command::FindFromService("chanserv/levels", bi, cmd))
|
||||||
{
|
{
|
||||||
source.Reply(" ");
|
source.Reply(" ");
|
||||||
source.Reply(_(
|
source.Reply(_(
|
||||||
@@ -768,14 +815,7 @@ class CommandCSLevels final
|
|||||||
|
|
||||||
ListFormatter::ListEntry entry;
|
ListFormatter::ListEntry entry;
|
||||||
entry["Name"] = p.name;
|
entry["Name"] = p.name;
|
||||||
|
entry["Level"] = LevelToString(source, j);
|
||||||
if (j == ACCESS_INVALID)
|
|
||||||
entry["Level"] = Language::Translate(source.GetAccount(), _("(disabled)"));
|
|
||||||
else if (j == ACCESS_FOUNDER)
|
|
||||||
entry["Level"] = Language::Translate(source.GetAccount(), _("(founder only)"));
|
|
||||||
else
|
|
||||||
entry["Level"] = Anope::ToString(j);
|
|
||||||
|
|
||||||
list.AddEntry(entry);
|
list.AddEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -852,14 +892,15 @@ public:
|
|||||||
source.Reply(_("The following feature/function names are available:"));
|
source.Reply(_("The following feature/function names are available:"));
|
||||||
|
|
||||||
ListFormatter list(source.GetAccount());
|
ListFormatter list(source.GetAccount());
|
||||||
list.AddColumn(_("Name")).AddColumn(_("Description"));
|
list.AddColumn(_("Name")).AddColumn(_("Default")).AddColumn(_("Description"));
|
||||||
list.SetFlexible(_("\002{name}\002: {description}"));
|
list.SetFlexible(_("\002{name}\002: defaults to {default} ({description})"));
|
||||||
|
|
||||||
for (const auto &p : PrivilegeManager::GetPrivileges())
|
for (const auto &p : PrivilegeManager::GetPrivileges())
|
||||||
{
|
{
|
||||||
ListFormatter::ListEntry entry;
|
ListFormatter::ListEntry entry;
|
||||||
entry["Name"] = p.name;
|
entry["Name"] = p.name;
|
||||||
entry["Description"] = Language::Translate(source.nc, p.desc.c_str());
|
entry["Default"] = LevelToString(source, defaultLevels[p.name]);
|
||||||
|
entry["Description"] = source.Translate(p.desc.c_str());
|
||||||
list.AddEntry(entry);
|
list.AddEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,10 +963,8 @@ public:
|
|||||||
{
|
{
|
||||||
defaultLevels.clear();
|
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");
|
const Anope::string &pname = priv.Get<const Anope::string>("name");
|
||||||
|
|
||||||
Privilege *p = PrivilegeManager::FindPrivilege(pname);
|
Privilege *p = PrivilegeManager::FindPrivilege(pname);
|
||||||
@@ -960,7 +999,7 @@ public:
|
|||||||
/* Access accessprovider is the only accessprovider with the concept of negative access,
|
/* Access accessprovider is the only accessprovider with the concept of negative access,
|
||||||
* so check they don't have negative access
|
* so check they don't have negative access
|
||||||
*/
|
*/
|
||||||
const AccessChanAccess *aca = anope_dynamic_static_cast<const AccessChanAccess *>(highest);
|
const auto *aca = anope_dynamic_static_cast<const AccessChanAccess *>(highest);
|
||||||
|
|
||||||
if (aca->level < 0)
|
if (aca->level < 0)
|
||||||
return EVENT_CONTINUE;
|
return EVENT_CONTINUE;
|
||||||
|
|||||||
@@ -143,46 +143,34 @@ struct AutoKickType final
|
|||||||
|
|
||||||
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
||||||
{
|
{
|
||||||
Anope::string sci, snc;
|
auto *ci = ChannelInfo::Find(data.Load("ci"));
|
||||||
uint64_t sncid = 0;
|
|
||||||
|
|
||||||
data["ci"] >> sci;
|
|
||||||
data["nc"] >> snc; // Deprecated 2.0 field
|
|
||||||
data["ncid"] >> sncid;
|
|
||||||
|
|
||||||
ChannelInfo *ci = ChannelInfo::Find(sci);
|
|
||||||
if (!ci)
|
if (!ci)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
const auto sncid = data.Load<uint64_t>("ncid");
|
||||||
|
auto *nc = sncid ? NickCore::FindId(sncid) : NickCore::Find(data.Load("nc"));
|
||||||
|
|
||||||
|
const auto screator = data.Load("creator");
|
||||||
|
const auto smask = data.Load("mask");
|
||||||
|
const auto sreason = data.Load("reason");
|
||||||
|
const auto saddtime = data.Load<time_t>("addtime");
|
||||||
|
const auto slastused = data.Load<time_t>("last_used");
|
||||||
|
|
||||||
ChanServ::AutoKick *ak;
|
ChanServ::AutoKick *ak;
|
||||||
auto *nc = sncid ? NickCore::FindId(sncid) : NickCore::Find(snc);
|
|
||||||
if (obj)
|
if (obj)
|
||||||
{
|
{
|
||||||
ak = anope_dynamic_static_cast<ChanServ::AutoKick *>(obj);
|
ak = anope_dynamic_static_cast<ChanServ::AutoKick *>(obj);
|
||||||
data["creator"] >> ak->creator;
|
ak->creator = screator;
|
||||||
data["reason"] >> ak->reason;
|
ak->reason = sreason;
|
||||||
ak->nc = nc;
|
ak->nc = nc;
|
||||||
data["mask"] >> ak->mask;
|
ak->mask = smask;
|
||||||
data["addtime"] >> ak->addtime;
|
ak->addtime = saddtime;
|
||||||
data["last_used"] >> ak->last_used;
|
ak->last_used = slastused;
|
||||||
}
|
}
|
||||||
|
else if (nc)
|
||||||
|
ak = ChanServ::akick_service->AddAKick(ci, screator, nc, sreason, saddtime, slastused);
|
||||||
else
|
else
|
||||||
{
|
ak = ChanServ::akick_service->AddAKick(ci, screator, smask, sreason, saddtime, slastused);
|
||||||
time_t addtime, lastused;
|
|
||||||
data["addtime"] >> addtime;
|
|
||||||
data["last_used"] >> lastused;
|
|
||||||
|
|
||||||
Anope::string screator, sreason, smask;
|
|
||||||
|
|
||||||
data["creator"] >> screator;
|
|
||||||
data["reason"] >> sreason;
|
|
||||||
data["mask"] >> smask;
|
|
||||||
|
|
||||||
if (nc)
|
|
||||||
ak = ChanServ::akick_service->AddAKick(ci, screator, nc, sreason, addtime, lastused);
|
|
||||||
else
|
|
||||||
ak = ChanServ::akick_service->AddAKick(ci, screator, smask, sreason, addtime, lastused);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ak;
|
return ak;
|
||||||
}
|
}
|
||||||
@@ -201,7 +189,7 @@ class CommandCSAKick final
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; )
|
for (auto it = c->users.begin(), it_end = c->users.end(); it != it_end; )
|
||||||
{
|
{
|
||||||
auto *memb = it->second;
|
auto *memb = it->second;
|
||||||
++it;
|
++it;
|
||||||
@@ -223,7 +211,7 @@ class CommandCSAKick final
|
|||||||
const NickAlias *na = NickAlias::Find(mask);
|
const NickAlias *na = NickAlias::Find(mask);
|
||||||
NickCore *nc = NULL;
|
NickCore *nc = NULL;
|
||||||
const ChanServ::AutoKick *akick;
|
const ChanServ::AutoKick *akick;
|
||||||
unsigned reasonmax = Config->GetModule("chanserv").Get<unsigned>("reasonmax", "200");
|
auto reasonmax = Config->GetModule("chanserv").Get<unsigned>("reasonmax", "200");
|
||||||
|
|
||||||
if (reason.length() > reasonmax)
|
if (reason.length() > reasonmax)
|
||||||
reason = reason.substr(0, reasonmax);
|
reason = reason.substr(0, reasonmax);
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ private:
|
|||||||
Anope::string mode;
|
Anope::string mode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
bool ticked = false;
|
||||||
|
|
||||||
TempBan(time_t seconds, Channel *c, const Anope::string &banmask, const Anope::string &mod)
|
TempBan(time_t seconds, Channel *c, const Anope::string &banmask, const Anope::string &mod)
|
||||||
: Timer(me, seconds)
|
: Timer(me, seconds)
|
||||||
, channel(c->name)
|
, channel(c->name)
|
||||||
@@ -51,11 +53,17 @@ public:
|
|||||||
&& bmask == this->mask;
|
&& bmask == this->mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tick() override
|
bool Tick() override
|
||||||
{
|
{
|
||||||
|
// We need to do this to prevent the remove-on-unban logic from double
|
||||||
|
// deleting the timer.
|
||||||
|
ticked = true;
|
||||||
|
|
||||||
Channel *c = Channel::Find(this->channel);
|
Channel *c = Channel::Find(this->channel);
|
||||||
if (c)
|
if (c)
|
||||||
c->RemoveMode(NULL, mode, this->mask);
|
c->RemoveMode(NULL, mode, this->mask);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -128,7 +136,7 @@ public:
|
|||||||
reason += " " + params[3];
|
reason += " " + params[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned reasonmax = Config->GetModule("chanserv").Get<unsigned>("reasonmax", "200");
|
auto reasonmax = Config->GetModule("chanserv").Get<unsigned>("reasonmax", "200");
|
||||||
if (reason.length() > reasonmax)
|
if (reason.length() > reasonmax)
|
||||||
reason = reason.substr(0, reasonmax);
|
reason = reason.substr(0, reasonmax);
|
||||||
|
|
||||||
@@ -211,7 +219,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
int matched = 0, kicked = 0;
|
int matched = 0, kicked = 0;
|
||||||
for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end;)
|
for (auto it = c->users.begin(), it_end = c->users.end(); it != it_end;)
|
||||||
{
|
{
|
||||||
auto *memb = it->second;
|
auto *memb = it->second;
|
||||||
++it;
|
++it;
|
||||||
@@ -264,11 +272,12 @@ public:
|
|||||||
source.Reply(_(
|
source.Reply(_(
|
||||||
"Bans a given nick or mask on a channel. An optional expiry may "
|
"Bans a given nick or mask on a channel. An optional expiry may "
|
||||||
"be given to cause services to remove the ban after a set amount "
|
"be given to cause services to remove the ban after a set amount "
|
||||||
"of time."
|
"of time. Channel founders may ban masks."
|
||||||
"\n\n"
|
|
||||||
"By default, limited to AOPs or those with level 5 access "
|
|
||||||
"and above on the channel. Channel founders may ban masks."
|
|
||||||
));
|
));
|
||||||
|
|
||||||
|
source.Reply(" ");
|
||||||
|
AccessProvider::SendAccess(source, "BAN");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -290,7 +299,7 @@ public:
|
|||||||
{
|
{
|
||||||
for (const auto *tempban : tempbans)
|
for (const auto *tempban : tempbans)
|
||||||
{
|
{
|
||||||
if (tempban->Matches(c, cmode, param))
|
if (!tempban->ticked && tempban->Matches(c, cmode, param))
|
||||||
{
|
{
|
||||||
delete tempban;
|
delete tempban;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class CommandCSClone final
|
|||||||
static void CopyAccess(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
|
static void CopyAccess(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
|
||||||
{
|
{
|
||||||
std::set<Anope::string> masks;
|
std::set<Anope::string> masks;
|
||||||
unsigned access_max = Config->GetModule("chanserv").Get<unsigned>("accessmax", "1000");
|
auto access_max = Config->GetModule("chanserv").Get<unsigned>("accessmax", "1000");
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
|
|
||||||
for (unsigned i = 0; i < target_ci->GetAccessCount(); ++i)
|
for (unsigned i = 0; i < target_ci->GetAccessCount(); ++i)
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ private:
|
|||||||
for (auto *user : users)
|
for (auto *user : users)
|
||||||
{
|
{
|
||||||
Anope::string mask = ci->GetIdealBan(user);
|
Anope::string mask = ci->GetIdealBan(user);
|
||||||
Anope::string reason = Language::Translate(user, _("RESTRICTED enforced by ")) + source.GetNick();
|
Anope::string reason = Anope::Format(Language::Translate(user, _("RESTRICTED enforced by %s")), source.GetNick().c_str());
|
||||||
ci->c->SetMode(NULL, "BAN", mask);
|
ci->c->SetMode(NULL, "BAN", mask);
|
||||||
ci->c->Kick(NULL, user, reason);
|
ci->c->Kick(NULL, user, reason);
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ private:
|
|||||||
for (auto *user : users)
|
for (auto *user : users)
|
||||||
{
|
{
|
||||||
Anope::string mask = ci->GetIdealBan(user);
|
Anope::string mask = ci->GetIdealBan(user);
|
||||||
Anope::string reason = Language::Translate(user, _("REGONLY enforced by ")) + source.GetNick();
|
Anope::string reason = Anope::Format(Language::Translate(user, _("REGONLY enforced by %s")), source.GetNick().c_str());
|
||||||
if (!ci->c->HasMode("REGISTEREDONLY"))
|
if (!ci->c->HasMode("REGISTEREDONLY"))
|
||||||
ci->c->SetMode(NULL, "BAN", mask);
|
ci->c->SetMode(NULL, "BAN", mask);
|
||||||
ci->c->Kick(NULL, user, reason);
|
ci->c->Kick(NULL, user, reason);
|
||||||
@@ -121,7 +121,7 @@ private:
|
|||||||
for (auto *user : users)
|
for (auto *user : users)
|
||||||
{
|
{
|
||||||
Anope::string mask = ci->GetIdealBan(user);
|
Anope::string mask = ci->GetIdealBan(user);
|
||||||
Anope::string reason = Language::Translate(user, _("SSLONLY enforced by ")) + source.GetNick();
|
Anope::string reason = Anope::Format(Language::Translate(user, _("SSLONLY enforced by %s")), source.GetNick().c_str());
|
||||||
if (!ci->c->HasMode("SSL"))
|
if (!ci->c->HasMode("SSL"))
|
||||||
ci->c->SetMode(NULL, "BAN", mask);
|
ci->c->SetMode(NULL, "BAN", mask);
|
||||||
ci->c->Kick(NULL, user, reason);
|
ci->c->Kick(NULL, user, reason);
|
||||||
@@ -149,7 +149,7 @@ private:
|
|||||||
|
|
||||||
for (auto *user : users)
|
for (auto *user : users)
|
||||||
{
|
{
|
||||||
Anope::string reason = Language::Translate(user, _("BANS enforced by ")) + source.GetNick();
|
Anope::string reason = Anope::Format(Language::Translate(user, _("BANS enforced by %s")), source.GetNick().c_str());
|
||||||
ci->c->Kick(NULL, user, reason);
|
ci->c->Kick(NULL, user, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ private:
|
|||||||
|
|
||||||
std::vector<User *> users;
|
std::vector<User *> users;
|
||||||
/* The newer users are at the end of the list, so kick users starting from the end */
|
/* The newer users are at the end of the list, so kick users starting from the end */
|
||||||
for (Channel::ChanUserList::reverse_iterator it = ci->c->users.rbegin(), it_end = ci->c->users.rend(); it != it_end; ++it)
|
for (auto it = ci->c->users.rbegin(), it_end = ci->c->users.rend(); it != it_end; ++it)
|
||||||
{
|
{
|
||||||
auto *memb = it->second;
|
auto *memb = it->second;
|
||||||
User *user = memb->user;
|
User *user = memb->user;
|
||||||
@@ -197,7 +197,7 @@ private:
|
|||||||
|
|
||||||
for (auto *user : users)
|
for (auto *user : users)
|
||||||
{
|
{
|
||||||
Anope::string reason = Language::Translate(user, _("LIMIT enforced by ")) + source.GetNick();
|
Anope::string reason = Anope::Format(Language::Translate(user, _("LIMIT enforced by %s")), source.GetNick().c_str());
|
||||||
ci->c->Kick(NULL, user, reason);
|
ci->c->Kick(NULL, user, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,31 +85,24 @@ EntryMsgImpl::~EntryMsgImpl()
|
|||||||
|
|
||||||
Serializable *EntryMsgTypeImpl::Unserialize(Serializable *obj, Serialize::Data &data) const
|
Serializable *EntryMsgTypeImpl::Unserialize(Serializable *obj, Serialize::Data &data) const
|
||||||
{
|
{
|
||||||
Anope::string sci, screator, smessage;
|
auto *ci = ChannelInfo::Find(data.Load("ci"));
|
||||||
time_t swhen;
|
|
||||||
|
|
||||||
data["ci"] >> sci;
|
|
||||||
data["creator"] >> screator;
|
|
||||||
data["message"] >> smessage;
|
|
||||||
|
|
||||||
ChannelInfo *ci = ChannelInfo::Find(sci);
|
|
||||||
if (!ci)
|
if (!ci)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
const auto screator = data.Load("creator");
|
||||||
|
const auto smessage = data.Load("message");
|
||||||
|
const auto swhen = data.Load<time_t>("when");
|
||||||
if (obj)
|
if (obj)
|
||||||
{
|
{
|
||||||
EntryMsgImpl *msg = anope_dynamic_static_cast<EntryMsgImpl *>(obj);
|
auto *msg = anope_dynamic_static_cast<EntryMsgImpl *>(obj);
|
||||||
msg->chan = ci->name;
|
msg->chan = ci->name;
|
||||||
data["creator"] >> msg->creator;
|
msg->creator = screator;
|
||||||
data["message"] >> msg->message;
|
msg->message = smessage;
|
||||||
data["when"] >> msg->when;
|
msg->when = swhen;
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *messages = ci->Require<ChanServ::EntryMessageList>(CHANSERV_ENTRY_MESSAGE_EXT);
|
auto *messages = ci->Require<ChanServ::EntryMessageList>(CHANSERV_ENTRY_MESSAGE_EXT);
|
||||||
|
|
||||||
data["when"] >> swhen;
|
|
||||||
|
|
||||||
auto *m = new EntryMsgImpl(ci, screator, smessage, swhen);
|
auto *m = new EntryMsgImpl(ci, screator, smessage, swhen);
|
||||||
(*messages)->push_back(m);
|
(*messages)->push_back(m);
|
||||||
return m;
|
return m;
|
||||||
@@ -306,11 +299,7 @@ public:
|
|||||||
{
|
{
|
||||||
Anope::map<Anope::string> tags;
|
Anope::map<Anope::string> tags;
|
||||||
if (timestamp)
|
if (timestamp)
|
||||||
{
|
tags["time"] = Anope::FormatISO8601(message->when, 0);
|
||||||
char timebuf[32];
|
|
||||||
strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S.000Z", gmtime(&message->when));
|
|
||||||
tags["time"] = timebuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (u->ShouldPrivmsg())
|
if (u->ShouldPrivmsg())
|
||||||
IRCD->SendContextPrivmsg(c->ci->WhoSends(), u, c, message->message, tags);
|
IRCD->SendContextPrivmsg(c->ci->WhoSends(), u, c, message->message, tags);
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public:
|
|||||||
void OnReload(Configuration::Conf &conf) override
|
void OnReload(Configuration::Conf &conf) override
|
||||||
{
|
{
|
||||||
const auto &block = conf.GetModule("chanstats");
|
const auto &block = conf.GetModule("chanstats");
|
||||||
prefix = block.Get<const Anope::string>("prefix", "anope_");
|
prefix = block.Get<const Anope::string>("prefix", "chanstats21_");
|
||||||
this->sql.SetServiceName(block.Get<const Anope::string>("engine"));
|
this->sql.SetServiceName(block.Get<const Anope::string>("engine"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ public:
|
|||||||
void OnReload(Configuration::Conf &conf) override
|
void OnReload(Configuration::Conf &conf) override
|
||||||
{
|
{
|
||||||
const auto &block = conf.GetModule("chanstats");
|
const auto &block = conf.GetModule("chanstats");
|
||||||
prefix = block.Get<const Anope::string>("prefix", "anope_");
|
prefix = block.Get<const Anope::string>("prefix", "chanstats21_");
|
||||||
this->sql.SetServiceName(block.Get<const Anope::string>("engine"));
|
this->sql.SetServiceName(block.Get<const Anope::string>("engine"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+145
-12
@@ -14,7 +14,15 @@
|
|||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
|
|
||||||
|
#define FLAGS_MIGRATED_1 _("\002%s\002 has been migrated to the %s access system.")
|
||||||
|
#define FLAGS_MIGRATED_N N_("\002%u\002 entry has been migrated to the %s access system.", "\002%u\002 entries have been migrated to the %s access system.")
|
||||||
|
#define FLAGS_NOT_MIGRATABLE_1 _("\002%s\002 can not be migrated to the %s access system because they have no migratable privileges.")
|
||||||
|
#define FLAGS_NOT_MIGRATABLE_N N_("\002%u\002 entry can not be migrated to the %s access system because they have no migratable privileges.", "\002%u\002 entries can not be migrated to the %s access system because they have no migratable privileges.")
|
||||||
|
#define FLAGS_NOT_MIGRATED_1 _("\002%s\002 can not be migrated to the %s access system because they have privileges that you do not.")
|
||||||
|
#define FLAGS_NOT_MIGRATED_N N_("\002%u\002 entry can not be migrated to the %s access system because they have privileges that you do not.", "\002%u\002 entries can not be migrated to the %s access system because they have privileges that you do not.")
|
||||||
|
|
||||||
static std::map<Anope::string, char> defaultFlags;
|
static std::map<Anope::string, char> defaultFlags;
|
||||||
|
static Anope::map<Anope::string> migrationRequires;
|
||||||
|
|
||||||
class FlagsChanAccess final
|
class FlagsChanAccess final
|
||||||
: public ChanAccess
|
: public ChanAccess
|
||||||
@@ -28,7 +36,7 @@ public:
|
|||||||
|
|
||||||
bool HasPriv(const Anope::string &priv) const override
|
bool HasPriv(const Anope::string &priv) const override
|
||||||
{
|
{
|
||||||
std::map<Anope::string, char>::iterator it = defaultFlags.find(priv);
|
auto it = defaultFlags.find(priv);
|
||||||
return it != defaultFlags.end() && this->flags.count(it->second) > 0;
|
return it != defaultFlags.end() && this->flags.count(it->second) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +84,13 @@ public:
|
|||||||
{
|
{
|
||||||
return new FlagsChanAccess(this);
|
return new FlagsChanAccess(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAccess(CommandSource& source, const Privilege *p, Anope::map<Anope::string> &access) override
|
||||||
|
{
|
||||||
|
auto it = defaultFlags.find(p->name);
|
||||||
|
if (it != defaultFlags.end())
|
||||||
|
access[_("Flag")] = Anope::ToString(it->second);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
FlagsAccessProvider *FlagsAccessProvider::ap;
|
FlagsAccessProvider *FlagsAccessProvider::ap;
|
||||||
|
|
||||||
@@ -286,7 +301,7 @@ class CommandCSFlags final
|
|||||||
if (current != NULL)
|
if (current != NULL)
|
||||||
{
|
{
|
||||||
ci->EraseAccess(current_idx - 1);
|
ci->EraseAccess(current_idx - 1);
|
||||||
FOREACH_MOD(OnAccessDel, (ci, source, current));
|
FOREACH_MOD(OnAccessDel, (ci, source, current, false));
|
||||||
delete current;
|
delete current;
|
||||||
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask;
|
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask;
|
||||||
source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str());
|
source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str());
|
||||||
@@ -301,10 +316,10 @@ class CommandCSFlags final
|
|||||||
ServiceReference<AccessProvider> provider("AccessProvider", "access/flags");
|
ServiceReference<AccessProvider> provider("AccessProvider", "access/flags");
|
||||||
if (!provider)
|
if (!provider)
|
||||||
return;
|
return;
|
||||||
FlagsChanAccess *access = anope_dynamic_static_cast<FlagsChanAccess *>(provider->Create());
|
auto *access = anope_dynamic_static_cast<FlagsChanAccess *>(provider->Create());
|
||||||
access->SetMask(mask, ci);
|
access->SetMask(mask, ci);
|
||||||
access->creator = source.GetNick();
|
access->creator = source.GetNick();
|
||||||
access->description = current ? current->description : description;
|
access->description = current && description.empty() ? current->description : description;
|
||||||
access->last_seen = current ? current->last_seen : 0;
|
access->last_seen = current ? current->last_seen : 0;
|
||||||
access->created = Anope::CurTime;
|
access->created = Anope::CurTime;
|
||||||
access->flags = current_flags;
|
access->flags = current_flags;
|
||||||
@@ -314,7 +329,7 @@ class CommandCSFlags final
|
|||||||
|
|
||||||
ci->AddAccess(access);
|
ci->AddAccess(access);
|
||||||
|
|
||||||
FOREACH_MOD(OnAccessAdd, (ci, source, access));
|
FOREACH_MOD(OnAccessAdd, (ci, source, access, false));
|
||||||
|
|
||||||
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to modify " << mask << "'s flags to " << access->AccessSerialize();
|
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to modify " << mask << "'s flags to " << access->AccessSerialize();
|
||||||
if (p != NULL)
|
if (p != NULL)
|
||||||
@@ -331,6 +346,7 @@ class CommandCSFlags final
|
|||||||
static void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
|
static void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
|
||||||
{
|
{
|
||||||
const Anope::string &arg = params.size() > 2 ? params[2] : "";
|
const Anope::string &arg = params.size() > 2 ? params[2] : "";
|
||||||
|
const auto show_all = params.size() > 3 && params[3].equals_ci("ALL");
|
||||||
|
|
||||||
if (!ci->GetAccessCount())
|
if (!ci->GetAccessCount())
|
||||||
{
|
{
|
||||||
@@ -348,6 +364,7 @@ class CommandCSFlags final
|
|||||||
});
|
});
|
||||||
|
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
|
unsigned foreign = 0;
|
||||||
for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
|
for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
|
||||||
{
|
{
|
||||||
const ChanAccess *access = ci->GetAccess(i);
|
const ChanAccess *access = ci->GetAccess(i);
|
||||||
@@ -368,6 +385,12 @@ class CommandCSFlags final
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!show_all && access->provider->name != "access/flags")
|
||||||
|
{
|
||||||
|
foreign++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ListFormatter::ListEntry entry;
|
ListFormatter::ListEntry entry;
|
||||||
++count;
|
++count;
|
||||||
entry["Number"] = Anope::ToString(i + 1);
|
entry["Number"] = Anope::ToString(i + 1);
|
||||||
@@ -390,6 +413,110 @@ class CommandCSFlags final
|
|||||||
else
|
else
|
||||||
source.Reply(_("End of access list - %d/%d entries shown."), count, ci->GetAccessCount());
|
source.Reply(_("End of access list - %d/%d entries shown."), count, ci->GetAccessCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (foreign)
|
||||||
|
{
|
||||||
|
const auto full_command = Anope::Format("%s %s LIST %s", source.command.c_str(),
|
||||||
|
ci->name.c_str(), arg.empty() ? "*" : arg.c_str()).nobreak();
|
||||||
|
|
||||||
|
source.Reply(foreign, CHAN_ACCESS_FOREIGN, foreign, full_command.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoMigrate(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
|
||||||
|
{
|
||||||
|
auto override = false;
|
||||||
|
|
||||||
|
unsigned migrated = 0, notmigratable = 0, notmigrated = 0;
|
||||||
|
Anope::string migratedmask, notmigratablemask, notmigratedmask;
|
||||||
|
const auto &entry = params.size() > 2 ? params[2] : "*";
|
||||||
|
for (auto idx = ci->GetAccessCount(); idx > 0; --idx)
|
||||||
|
{
|
||||||
|
auto *access = ci->GetAccess(idx - 1);
|
||||||
|
if (access->provider->name == "access/flags")
|
||||||
|
continue; // Already using flags.
|
||||||
|
|
||||||
|
if (!Anope::Match(access->Mask(), entry))
|
||||||
|
continue; // Not this entry.
|
||||||
|
|
||||||
|
std::set<char> newflags;
|
||||||
|
for (auto &[priv, flag] : defaultFlags)
|
||||||
|
{
|
||||||
|
if (!access->HasPriv(priv))
|
||||||
|
continue; // Source doesn't have this flag.
|
||||||
|
|
||||||
|
// Check that the source has access to set this entry.
|
||||||
|
const auto source_access = source.AccessFor(ci);
|
||||||
|
if (!override && !source_access.HasPriv(priv) && !source_access.founder)
|
||||||
|
{
|
||||||
|
if (!source.HasPriv("chanserv/access/modify"))
|
||||||
|
{
|
||||||
|
notmigrated++;
|
||||||
|
notmigratedmask = access->Mask();
|
||||||
|
Log(LOG_DEBUG) << source.GetNick() << " does not have the access to migrate " << access->Mask() << " to flags";
|
||||||
|
continue; // No privs
|
||||||
|
}
|
||||||
|
|
||||||
|
override = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto req = migrationRequires.find(priv);
|
||||||
|
if (req != migrationRequires.end() && !access->HasPriv(req->second))
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG) << access->Mask() << " has " << priv << " but not " << req->second << " so it will be lost on migration to flags";
|
||||||
|
continue; // Required flag missing.
|
||||||
|
}
|
||||||
|
|
||||||
|
newflags.insert(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newflags.empty())
|
||||||
|
{
|
||||||
|
notmigratable++;
|
||||||
|
notmigratablemask = access->Mask();
|
||||||
|
Log(LOG_DEBUG) << access->Mask() << " has " << access->AccessSerialize() << " that can not be migrated to flags";
|
||||||
|
continue; // No privs that are migratable
|
||||||
|
}
|
||||||
|
|
||||||
|
migrated++;
|
||||||
|
migratedmask = access->Mask();
|
||||||
|
|
||||||
|
auto *newaccess = anope_dynamic_static_cast<FlagsChanAccess *>(FlagsAccessProvider::ap->Create());
|
||||||
|
newaccess->SetMask(access->Mask(), ci);
|
||||||
|
newaccess->created = access->created;
|
||||||
|
newaccess->creator = access->creator;
|
||||||
|
newaccess->description = access->description;
|
||||||
|
newaccess->flags = newflags;
|
||||||
|
newaccess->last_seen = access->last_seen;
|
||||||
|
|
||||||
|
ci->EraseAccess(idx - 1);
|
||||||
|
FOREACH_MOD(OnAccessDel, (ci, source, access, true));
|
||||||
|
delete access;
|
||||||
|
|
||||||
|
ci->AddAccess(newaccess);
|
||||||
|
FOREACH_MOD(OnAccessAdd, (ci, source, newaccess, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (migrated == 1)
|
||||||
|
{
|
||||||
|
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to migrate " << migratedmask;
|
||||||
|
source.Reply(FLAGS_MIGRATED_1, migratedmask.c_str(), source.command.nobreak().c_str());
|
||||||
|
}
|
||||||
|
else if (migrated > 1)
|
||||||
|
{
|
||||||
|
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to migrate " << migrated << " access entries";
|
||||||
|
source.Reply(migrated, FLAGS_MIGRATED_N, migrated, source.command.nobreak().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notmigratable == 1)
|
||||||
|
source.Reply(FLAGS_NOT_MIGRATABLE_1, notmigratablemask.c_str(), source.command.nobreak().c_str());
|
||||||
|
else if (notmigratable > 1)
|
||||||
|
source.Reply(notmigratable, FLAGS_NOT_MIGRATABLE_N, notmigratable, source.command.nobreak().c_str());
|
||||||
|
|
||||||
|
if (notmigrated == 1)
|
||||||
|
source.Reply(FLAGS_NOT_MIGRATED_1, notmigratedmask.c_str(), source.command.nobreak().c_str());
|
||||||
|
else if (notmigrated > 1)
|
||||||
|
source.Reply(notmigrated, FLAGS_NOT_MIGRATED_N, notmigrated, source.command.nobreak().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoClear(CommandSource &source, ChannelInfo *ci)
|
void DoClear(CommandSource &source, ChannelInfo *ci)
|
||||||
@@ -414,7 +541,8 @@ public:
|
|||||||
{
|
{
|
||||||
this->SetDesc(_("Modify the list of privileged users"));
|
this->SetDesc(_("Modify the list of privileged users"));
|
||||||
this->SetSyntax(_("\037channel\037 [MODIFY] \037mask\037 \037changes\037 [\037description\037]"));
|
this->SetSyntax(_("\037channel\037 [MODIFY] \037mask\037 \037changes\037 [\037description\037]"));
|
||||||
this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | +\037flags\037]"));
|
this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | +\037flags\037] [ALL]"));
|
||||||
|
this->SetSyntax(_("\037channel\037 MIGRATE [\037mask\037]"));
|
||||||
this->SetSyntax(_("\037channel\037 CLEAR"));
|
this->SetSyntax(_("\037channel\037 CLEAR"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,6 +575,8 @@ public:
|
|||||||
source.Reply(READ_ONLY_MODE);
|
source.Reply(READ_ONLY_MODE);
|
||||||
else if (is_list)
|
else if (is_list)
|
||||||
this->DoList(source, ci, params);
|
this->DoList(source, ci, params);
|
||||||
|
else if (cmd.equals_ci("MIGRATE"))
|
||||||
|
this->DoMigrate(source, ci, params);
|
||||||
else if (cmd.equals_ci("CLEAR"))
|
else if (cmd.equals_ci("CLEAR"))
|
||||||
this->DoClear(source, ci);
|
this->DoClear(source, ci);
|
||||||
else
|
else
|
||||||
@@ -487,7 +617,8 @@ public:
|
|||||||
"The \002LIST\002 command allows you to list existing entries on the channel access list. "
|
"The \002LIST\002 command allows you to list existing entries on the channel access list. "
|
||||||
"If a mask is given, the mask is wildcard matched against all existing entries on the "
|
"If a mask is given, the mask is wildcard matched against all existing entries on the "
|
||||||
"access list, and only those entries are returned. If a set of flags is given, only those "
|
"access list, and only those entries are returned. If a set of flags is given, only those "
|
||||||
"on the access list with the specified flags are returned."
|
"on the access list with the specified flags are returned. The \002ALL\002 option allows "
|
||||||
|
"listing entries from other access systems as well as flags."
|
||||||
"\n\n"
|
"\n\n"
|
||||||
"The \002CLEAR\002 command clears the channel access list. This requires channel founder access."
|
"The \002CLEAR\002 command clears the channel access list. This requires channel founder access."
|
||||||
"\n\n"
|
"\n\n"
|
||||||
@@ -505,7 +636,7 @@ public:
|
|||||||
Privilege *p = PrivilegeManager::FindPrivilege(priv);
|
Privilege *p = PrivilegeManager::FindPrivilege(priv);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
continue;
|
continue;
|
||||||
source.Reply(" %c - %s", flag, Language::Translate(source.nc, p->desc.c_str()));
|
source.Reply(" %c - %s", flag, source.Translate(p->desc.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -530,10 +661,8 @@ public:
|
|||||||
{
|
{
|
||||||
defaultFlags.clear();
|
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");
|
const Anope::string &pname = priv.Get<const Anope::string>("name");
|
||||||
|
|
||||||
Privilege *p = PrivilegeManager::FindPrivilege(pname);
|
Privilege *p = PrivilegeManager::FindPrivilege(pname);
|
||||||
@@ -544,6 +673,10 @@ public:
|
|||||||
if (value.empty())
|
if (value.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
const auto &migration_requires = priv.Get<const Anope::string>("flag_migration_requires");
|
||||||
|
if (!migration_requires.empty())
|
||||||
|
migrationRequires[p->name] = migration_requires;
|
||||||
|
|
||||||
defaultFlags[p->name] = value[0];
|
defaultFlags[p->name] = value[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,11 +95,12 @@ public:
|
|||||||
source.Reply(_(
|
source.Reply(_(
|
||||||
"Tells %s to invite you or an optionally specified "
|
"Tells %s to invite you or an optionally specified "
|
||||||
"nick into the given channel."
|
"nick into the given channel."
|
||||||
"\n\n"
|
|
||||||
"By default, limited to AOPs or those with level 5 access and above "
|
|
||||||
"on the channel."
|
|
||||||
),
|
),
|
||||||
source.service->nick.c_str());
|
source.service->nick.c_str());
|
||||||
|
|
||||||
|
source.Reply(" ");
|
||||||
|
AccessProvider::SendAccess(source, "INVITE");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned reasonmax = Config->GetModule("chanserv").Get<unsigned>("reasonmax", "200");
|
auto reasonmax = Config->GetModule("chanserv").Get<unsigned>("reasonmax", "200");
|
||||||
if (reason.length() > reasonmax)
|
if (reason.length() > reasonmax)
|
||||||
reason = reason.substr(0, reasonmax);
|
reason = reason.substr(0, reasonmax);
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ public:
|
|||||||
Log(LOG_COMMAND, source, this, ci) << "for " << mask;
|
Log(LOG_COMMAND, source, this, ci) << "for " << mask;
|
||||||
|
|
||||||
int matched = 0, kicked = 0;
|
int matched = 0, kicked = 0;
|
||||||
for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end;)
|
for (auto it = c->users.begin(), it_end = c->users.end(); it != it_end;)
|
||||||
{
|
{
|
||||||
auto *memb = it->second;
|
auto *memb = it->second;
|
||||||
++it;
|
++it;
|
||||||
@@ -134,12 +134,11 @@ public:
|
|||||||
{
|
{
|
||||||
this->SendSyntax(source);
|
this->SendSyntax(source);
|
||||||
source.Reply(" ");
|
source.Reply(" ");
|
||||||
source.Reply(_(
|
source.Reply(_("Kicks a specified nick from a channel. Channel founders can also specify masks."));
|
||||||
"Kicks a specified nick from a channel."
|
|
||||||
"\n\n"
|
source.Reply(" ");
|
||||||
"By default, limited to AOPs or those with level 5 access "
|
AccessProvider::SendAccess(source, "KICK");
|
||||||
"and above on the channel. Channel founders can also specify masks."
|
|
||||||
));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Anope::string spattern = "#" + pattern;
|
Anope::string spattern = "#" + pattern;
|
||||||
unsigned listmax = Config->GetModule(this->owner).Get<unsigned>("listmax", "50");
|
auto listmax = Config->GetModule(this->owner).Get<unsigned>("listmax", "50");
|
||||||
|
|
||||||
source.Reply(_("List of entries matching \002%s\002:"), pattern.c_str());
|
source.Reply(_("List of entries matching \002%s\002:"), pattern.c_str());
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ public:
|
|||||||
ListFormatter::ListEntry entry;
|
ListFormatter::ListEntry entry;
|
||||||
entry["Name"] = (isnoexpire ? "!" : "") + ci->name;
|
entry["Name"] = (isnoexpire ? "!" : "") + ci->name;
|
||||||
if (ci->HasExt("CS_SUSPENDED"))
|
if (ci->HasExt("CS_SUSPENDED"))
|
||||||
entry["Description"] = Language::Translate(source.GetAccount(), _("[Suspended]"));
|
entry["Description"] = source.Translate(_("[Suspended]"));
|
||||||
else
|
else
|
||||||
entry["Description"] = ci->desc;
|
entry["Description"] = ci->desc;
|
||||||
list.AddEntry(entry);
|
list.AddEntry(entry);
|
||||||
@@ -245,7 +245,7 @@ public:
|
|||||||
|
|
||||||
BotInfo *bi;
|
BotInfo *bi;
|
||||||
Anope::string cmd;
|
Anope::string cmd;
|
||||||
if (Command::FindCommandFromService("chanserv/list", bi, cmd))
|
if (Command::FindFromService("chanserv/list", bi, cmd))
|
||||||
{
|
{
|
||||||
source.Reply(_("When \002private\002 is set, the channel will not appear in %s's %s command."),
|
source.Reply(_("When \002private\002 is set, the channel will not appear in %s's %s command."),
|
||||||
bi->nick.c_str(), cmd.c_str());
|
bi->nick.c_str(), cmd.c_str());
|
||||||
|
|||||||
@@ -64,10 +64,7 @@ struct LogSettingTypeImpl final
|
|||||||
|
|
||||||
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
||||||
{
|
{
|
||||||
Anope::string sci;
|
auto *ci = ChannelInfo::Find(data.Load("ci"));
|
||||||
data["ci"] >> sci;
|
|
||||||
|
|
||||||
ChannelInfo *ci = ChannelInfo::Find(sci);
|
|
||||||
if (ci == NULL)
|
if (ci == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -82,13 +79,13 @@ struct LogSettingTypeImpl final
|
|||||||
}
|
}
|
||||||
|
|
||||||
ls->chan = ci->name;
|
ls->chan = ci->name;
|
||||||
data["service_name"] >> ls->service_name;
|
ls->service_name = data.Load("service_name");
|
||||||
data["command_service"] >> ls->command_service;
|
ls->command_service = data.Load("command_service");
|
||||||
data["command_name"] >> ls->command_name;
|
ls->command_name = data.Load("command_name");
|
||||||
data["method"] >> ls->method;
|
ls->method = data.Load("method");
|
||||||
data["extra"] >> ls->extra;
|
ls->extra = data.Load("extra");
|
||||||
data["creator"] >> ls->creator;
|
ls->creator = data.Load("creator");
|
||||||
data["created"] >> ls->created;
|
ls->created = data.Load<time_t>("created");
|
||||||
|
|
||||||
return ls;
|
return ls;
|
||||||
}
|
}
|
||||||
@@ -334,10 +331,8 @@ public:
|
|||||||
const auto &block = conf.GetModule(this);
|
const auto &block = conf.GetModule(this);
|
||||||
defaults.clear();
|
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;
|
LogDefault ld;
|
||||||
|
|
||||||
ld.service = def.Get<const Anope::string>("service");
|
ld.service = def.Get<const Anope::string>("service");
|
||||||
|
|||||||
@@ -270,11 +270,7 @@ void ModeLockTypeImpl::Serialize(Serializable *obj, Serialize::Data &data) const
|
|||||||
|
|
||||||
Serializable *ModeLockTypeImpl::Unserialize(Serializable *obj, Serialize::Data &data) const
|
Serializable *ModeLockTypeImpl::Unserialize(Serializable *obj, Serialize::Data &data) const
|
||||||
{
|
{
|
||||||
Anope::string sci;
|
auto *ci = ChannelInfo::Find(data.Load("ci"));
|
||||||
|
|
||||||
data["ci"] >> sci;
|
|
||||||
|
|
||||||
ChannelInfo *ci = ChannelInfo::Find(sci);
|
|
||||||
if (!ci)
|
if (!ci)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -287,11 +283,11 @@ Serializable *ModeLockTypeImpl::Unserialize(Serializable *obj, Serialize::Data &
|
|||||||
ml->ci = ci->name;
|
ml->ci = ci->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
data["set"] >> ml->set;
|
ml->set = data.Load<bool>("set");
|
||||||
data["created"] >> ml->created;
|
ml->created = data.Load<time_t>("created");
|
||||||
data["setter"] >> ml->setter;
|
ml->setter = data.Load("setter");
|
||||||
data["name"] >> ml->name;
|
ml->name = data.Load("name");
|
||||||
data["param"] >> ml->param;
|
ml->param = data.Load("param");
|
||||||
|
|
||||||
if (!obj)
|
if (!obj)
|
||||||
ci->Require<ModeLocksImpl>(CHANSERV_MODE_LOCK_EXT)->mlocks->push_back(ml);
|
ci->Require<ModeLocksImpl>(CHANSERV_MODE_LOCK_EXT)->mlocks->push_back(ml);
|
||||||
@@ -601,7 +597,7 @@ class CommandCSMode final
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Channel::ChanUserList::const_iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end;)
|
for (auto it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end;)
|
||||||
{
|
{
|
||||||
auto *memb = it->second;
|
auto *memb = it->second;
|
||||||
++it;
|
++it;
|
||||||
@@ -949,9 +945,9 @@ public:
|
|||||||
if (!m.second.empty())
|
if (!m.second.empty())
|
||||||
{
|
{
|
||||||
if (m.first)
|
if (m.first)
|
||||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Gives you or the specified nick %s status on a channel")), m.second.c_str());
|
return Anope::Format(source.Translate(_("Gives you or the specified nick %s status on a channel")), m.second.c_str());
|
||||||
else
|
else
|
||||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Removes %s status from you or the specified nick on a channel")), m.second.c_str());
|
return Anope::Format(source.Translate(_("Removes %s status from you or the specified nick on a channel")), m.second.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return "";
|
return "";
|
||||||
@@ -1003,10 +999,8 @@ public:
|
|||||||
{
|
{
|
||||||
modes.clear();
|
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"),
|
const Anope::string &cname = block.Get<const Anope::string>("name"),
|
||||||
&cmd = block.Get<const Anope::string>("command");
|
&cmd = block.Get<const Anope::string>("command");
|
||||||
|
|
||||||
@@ -1102,7 +1096,7 @@ public:
|
|||||||
Anope::string param;
|
Anope::string param;
|
||||||
if (cm->type == MODE_PARAM)
|
if (cm->type == MODE_PARAM)
|
||||||
{
|
{
|
||||||
ChannelModeParam *cmp = anope_dynamic_static_cast<ChannelModeParam *>(cm);
|
auto *cmp = anope_dynamic_static_cast<ChannelModeParam *>(cm);
|
||||||
if (add || !cmp->minus_no_arg)
|
if (add || !cmp->minus_no_arg)
|
||||||
{
|
{
|
||||||
sep.GetToken(param);
|
sep.GetToken(param);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public:
|
|||||||
{
|
{
|
||||||
const Anope::string &chan = params[0];
|
const Anope::string &chan = params[0];
|
||||||
const Anope::string &chdesc = params.size() > 1 ? params[1] : "";
|
const Anope::string &chdesc = params.size() > 1 ? params[1] : "";
|
||||||
unsigned maxregistered = Config->GetModule("chanserv").Get<unsigned>("maxregistered");
|
auto maxregistered = Config->GetModule("chanserv").Get<unsigned>("maxregistered");
|
||||||
|
|
||||||
User *u = source.GetUser();
|
User *u = source.GetUser();
|
||||||
NickCore *nc = source.nc;
|
NickCore *nc = source.nc;
|
||||||
@@ -105,7 +105,7 @@ public:
|
|||||||
|
|
||||||
BotInfo *bi;
|
BotInfo *bi;
|
||||||
Anope::string cmd;
|
Anope::string cmd;
|
||||||
if (Command::FindCommandFromService("chanserv/access", bi, cmd))
|
if (Command::FindFromService("chanserv/access", bi, cmd))
|
||||||
{
|
{
|
||||||
source.Reply(" ");
|
source.Reply(" ");
|
||||||
source.Reply(_(
|
source.Reply(_(
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct SeenInfo final
|
|||||||
|
|
||||||
~SeenInfo() override
|
~SeenInfo() override
|
||||||
{
|
{
|
||||||
database_map::iterator iter = database.find(nick);
|
auto iter = database.find(nick);
|
||||||
if (iter != database.end() && iter->second == this)
|
if (iter != database.end() && iter->second == this)
|
||||||
database.erase(iter);
|
database.erase(iter);
|
||||||
}
|
}
|
||||||
@@ -111,9 +111,7 @@ struct SeenInfoType final
|
|||||||
|
|
||||||
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
||||||
{
|
{
|
||||||
Anope::string snick;
|
const auto snick = data.Load("nick");
|
||||||
|
|
||||||
data["nick"] >> snick;
|
|
||||||
|
|
||||||
SeenInfo *s;
|
SeenInfo *s;
|
||||||
if (obj)
|
if (obj)
|
||||||
@@ -127,14 +125,12 @@ struct SeenInfoType final
|
|||||||
}
|
}
|
||||||
|
|
||||||
s->nick = snick;
|
s->nick = snick;
|
||||||
data["vhost"] >> s->vhost;
|
s->vhost = data.Load("vhost");
|
||||||
Anope::string n;
|
s->type = StringToType(data.Load("type"));
|
||||||
data["type"] >> n;
|
s->nick2 = data.Load("nick2");
|
||||||
s->type = StringToType(n);
|
s->channel = data.Load("channel");
|
||||||
data["nick2"] >> s->nick2;
|
s->message = data.Load("message");
|
||||||
data["channel"] >> s->channel;
|
s->last = data.Load<time_t>("last");
|
||||||
data["message"] >> s->message;
|
|
||||||
data["last"] >> s->last;
|
|
||||||
|
|
||||||
if (!obj)
|
if (!obj)
|
||||||
database[s->nick] = s;
|
database[s->nick] = s;
|
||||||
@@ -144,7 +140,7 @@ struct SeenInfoType final
|
|||||||
|
|
||||||
static SeenInfo *FindInfo(const Anope::string &nick)
|
static SeenInfo *FindInfo(const Anope::string &nick)
|
||||||
{
|
{
|
||||||
database_map::iterator iter = database.find(nick);
|
auto iter = database.find(nick);
|
||||||
if (iter != database.end())
|
if (iter != database.end())
|
||||||
return iter->second;
|
return iter->second;
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -203,7 +199,7 @@ public:
|
|||||||
time = Anope::CurTime - time;
|
time = Anope::CurTime - time;
|
||||||
database_map::iterator buf;
|
database_map::iterator buf;
|
||||||
size_t counter = 0;
|
size_t counter = 0;
|
||||||
for (database_map::iterator it = database.begin(), it_end = database.end(); it != it_end;)
|
for (auto it = database.begin(), it_end = database.end(); it != it_end;)
|
||||||
{
|
{
|
||||||
buf = it;
|
buf = it;
|
||||||
++it;
|
++it;
|
||||||
@@ -285,7 +281,7 @@ public:
|
|||||||
if (u2)
|
if (u2)
|
||||||
onlinestatus = ".";
|
onlinestatus = ".";
|
||||||
else
|
else
|
||||||
onlinestatus = Anope::Format(Language::Translate(source.nc, _(" but %s mysteriously dematerialized.")), target.c_str());
|
onlinestatus = Anope::Format(source.Translate(_(" but %s mysteriously dematerialized.")), target.c_str());
|
||||||
|
|
||||||
Anope::string timebuf = Anope::Duration(Anope::CurTime - info->last, source.nc);
|
Anope::string timebuf = Anope::Duration(Anope::CurTime - info->last, source.nc);
|
||||||
Anope::string timebuf2 = Anope::strftime(info->last, source.nc, true);
|
Anope::string timebuf2 = Anope::strftime(info->last, source.nc, true);
|
||||||
@@ -299,9 +295,9 @@ public:
|
|||||||
{
|
{
|
||||||
u2 = User::Find(info->nick2, true);
|
u2 = User::Find(info->nick2, true);
|
||||||
if (u2)
|
if (u2)
|
||||||
onlinestatus = Anope::Format(Language::Translate(source.nc, _(". %s is still online.")), u2->nick.c_str());
|
onlinestatus = Anope::Format(source.Translate(_(". %s is still online.")), u2->nick.c_str());
|
||||||
else
|
else
|
||||||
onlinestatus = Anope::Format(Language::Translate(source.nc, _(", but %s mysteriously dematerialized.")), info->nick2.c_str());
|
onlinestatus = Anope::Format(source.Translate(_(", but %s mysteriously dematerialized.")), info->nick2.c_str());
|
||||||
|
|
||||||
source.Reply(_("%s (%s) was last seen changing nick to %s %s ago%s"),
|
source.Reply(_("%s (%s) was last seen changing nick to %s %s ago%s"),
|
||||||
target.c_str(), info->vhost.c_str(), info->nick2.c_str(), timebuf.c_str(), onlinestatus.c_str());
|
target.c_str(), info->vhost.c_str(), info->nick2.c_str(), timebuf.c_str(), onlinestatus.c_str());
|
||||||
@@ -379,9 +375,9 @@ public:
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
auto previous_size = database.size();
|
auto previous_size = database.size();
|
||||||
for (database_map::iterator it = database.begin(), it_end = database.end(); it != it_end;)
|
for (auto it = database.begin(), it_end = database.end(); it != it_end;)
|
||||||
{
|
{
|
||||||
database_map::iterator cur = it;
|
auto cur = it;
|
||||||
++it;
|
++it;
|
||||||
|
|
||||||
if ((Anope::CurTime - cur->second->last) > purgetime)
|
if ((Anope::CurTime - cur->second->last) > purgetime)
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
NickCore *nc = na->nc;
|
NickCore *nc = na->nc;
|
||||||
unsigned max_reg = Config->GetModule("chanserv").Get<unsigned>("maxregistered");
|
auto max_reg = Config->GetModule("chanserv").Get<unsigned>("maxregistered");
|
||||||
if (max_reg && nc->channelcount >= max_reg && !source.HasPriv("chanserv/no-register-limit"))
|
if (max_reg && nc->channelcount >= max_reg && !source.HasPriv("chanserv/no-register-limit"))
|
||||||
{
|
{
|
||||||
source.Reply(_("\002%s\002 has too many channels registered."), na->nick.c_str());
|
source.Reply(_("\002%s\002 has too many channels registered."), na->nick.c_str());
|
||||||
@@ -1018,7 +1018,7 @@ public:
|
|||||||
"channel will be dropped."
|
"channel will be dropped."
|
||||||
));
|
));
|
||||||
|
|
||||||
unsigned max_reg = Config->GetModule("chanserv").Get<unsigned>("maxregistered");
|
auto max_reg = Config->GetModule("chanserv").Get<unsigned>("maxregistered");
|
||||||
if (max_reg)
|
if (max_reg)
|
||||||
{
|
{
|
||||||
source.Reply(" ");
|
source.Reply(" ");
|
||||||
@@ -1112,7 +1112,7 @@ class CSSet final
|
|||||||
if (s->GetSerializableType()->GetName() != CHANNELINFO_TYPE)
|
if (s->GetSerializableType()->GetName() != CHANNELINFO_TYPE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ChannelInfo *ci = anope_dynamic_static_cast<const ChannelInfo *>(s);
|
const auto *ci = anope_dynamic_static_cast<const ChannelInfo *>(s);
|
||||||
Anope::string modes;
|
Anope::string modes;
|
||||||
for (const auto &[last_mode, last_data] : ci->last_modes)
|
for (const auto &[last_mode, last_data] : ci->last_modes)
|
||||||
{
|
{
|
||||||
@@ -1138,11 +1138,11 @@ class CSSet final
|
|||||||
if (s->GetSerializableType()->GetName() != CHANNELINFO_TYPE)
|
if (s->GetSerializableType()->GetName() != CHANNELINFO_TYPE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChannelInfo *ci = anope_dynamic_static_cast<ChannelInfo *>(s);
|
auto *ci = anope_dynamic_static_cast<ChannelInfo *>(s);
|
||||||
Anope::string modes;
|
|
||||||
data["last_modes"] >> modes;
|
|
||||||
ci->last_modes.clear();
|
ci->last_modes.clear();
|
||||||
for (spacesepstream sep(modes); sep.GetToken(modes);)
|
|
||||||
|
spacesepstream sep(data.Load("last_modes"));
|
||||||
|
for (Anope::string modes; sep.GetToken(modes);)
|
||||||
{
|
{
|
||||||
if (modes[0] == '+')
|
if (modes[0] == '+')
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -77,23 +77,20 @@ struct CSMiscDataType
|
|||||||
|
|
||||||
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
||||||
{
|
{
|
||||||
Anope::string sci, sname, sdata;
|
auto *ci = ChannelInfo::Find(data.Load("ci"));
|
||||||
|
|
||||||
data["ci"] >> sci;
|
|
||||||
data["name"] >> sname;
|
|
||||||
data["data"] >> sdata;
|
|
||||||
|
|
||||||
ChannelInfo *ci = ChannelInfo::Find(sci);
|
|
||||||
if (ci == NULL)
|
if (ci == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
const auto sname = data.Load("name");
|
||||||
|
const auto sdata = data.Load("data");
|
||||||
|
|
||||||
CSMiscData *d = NULL;
|
CSMiscData *d = NULL;
|
||||||
if (obj)
|
if (obj)
|
||||||
{
|
{
|
||||||
d = anope_dynamic_static_cast<CSMiscData *>(obj);
|
d = anope_dynamic_static_cast<CSMiscData *>(obj);
|
||||||
d->object = ci->name;
|
d->object = ci->name;
|
||||||
data["name"] >> d->name;
|
d->name = sname;
|
||||||
data["data"] >> d->data;
|
d->data = sdata;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -225,7 +222,7 @@ public:
|
|||||||
{
|
{
|
||||||
this->SendSyntax(source);
|
this->SendSyntax(source);
|
||||||
source.Reply(" ");
|
source.Reply(" ");
|
||||||
source.Reply("%s", Language::Translate(source.nc, it->second.description.c_str()));
|
source.Reply("%s", source.Translate(it->second.description.c_str()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -240,8 +237,8 @@ public:
|
|||||||
|
|
||||||
this->ClearSyntax();
|
this->ClearSyntax();
|
||||||
this->SetSyntax(Anope::Format(
|
this->SetSyntax(Anope::Format(
|
||||||
Language::Translate(source.nc, _("\037channel\037 [\037%s\037]")),
|
source.Translate(_("\037channel\037 [\037%s\037]")),
|
||||||
Language::Translate(source.nc, value)
|
source.Translate(value)
|
||||||
));
|
));
|
||||||
|
|
||||||
Command::SendSyntax(source);
|
Command::SendSyntax(source);
|
||||||
@@ -271,9 +268,9 @@ public:
|
|||||||
void OnReload(Configuration::Conf &conf) override
|
void OnReload(Configuration::Conf &conf) override
|
||||||
{
|
{
|
||||||
command_data.clear();
|
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")
|
if (block.Get<const Anope::string>("command") != "chanserv/set/misc")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ class StatusUpdate final
|
|||||||
: public Module
|
: public Module
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
void OnAccessChange(ChannelInfo *ci, ChanAccess *access, bool adding)
|
void OnAccessChange(ChannelInfo *ci, ChanAccess *access, bool migrated, bool adding)
|
||||||
{
|
{
|
||||||
if (!ci->c)
|
if (!ci->c || migrated)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (const auto &[_, uc] : ci->c->users)
|
for (const auto &[_, uc] : ci->c->users)
|
||||||
@@ -50,14 +50,14 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnAccessAdd(ChannelInfo *ci, CommandSource &, ChanAccess *access) override
|
void OnAccessAdd(ChannelInfo *ci, CommandSource &, ChanAccess *access, bool migrated) override
|
||||||
{
|
{
|
||||||
OnAccessChange(ci, access, true);
|
OnAccessChange(ci, access, migrated, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnAccessDel(ChannelInfo *ci, CommandSource &, ChanAccess *access) override
|
void OnAccessDel(ChannelInfo *ci, CommandSource &, ChanAccess *access, bool migrated) override
|
||||||
{
|
{
|
||||||
OnAccessChange(ci, access, false);
|
OnAccessChange(ci, access, migrated, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -42,25 +42,22 @@ struct CSSuspendInfoType final
|
|||||||
|
|
||||||
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
|
||||||
{
|
{
|
||||||
Anope::string schan;
|
|
||||||
data["chan"] >> schan;
|
|
||||||
|
|
||||||
CSSuspendInfo *si;
|
CSSuspendInfo *si;
|
||||||
if (obj)
|
if (obj)
|
||||||
si = anope_dynamic_static_cast<CSSuspendInfo *>(obj);
|
si = anope_dynamic_static_cast<CSSuspendInfo *>(obj);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ChannelInfo *ci = ChannelInfo::Find(schan);
|
auto *ci = ChannelInfo::Find(data.Load("chan"));
|
||||||
if (!ci)
|
if (!ci)
|
||||||
return NULL;
|
return NULL;
|
||||||
si = ci->Extend<CSSuspendInfo>("CS_SUSPENDED");
|
si = ci->Extend<CSSuspendInfo>("CS_SUSPENDED");
|
||||||
data["chan"] >> si->what;
|
si->what = ci->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
data["by"] >> si->by;
|
si->by = data.Load("by");
|
||||||
data["reason"] >> si->reason;
|
si->reason = data.Load("reason");
|
||||||
data["time"] >> si->when;
|
si->when = data.Load<time_t>("time");
|
||||||
data["expires"] >> si->expires;
|
si->expires = data.Load<time_t>("expires");
|
||||||
return si;
|
return si;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -116,7 +113,7 @@ public:
|
|||||||
for (auto idx = reason_idx; idx < params.size(); ++idx)
|
for (auto idx = reason_idx; idx < params.size(); ++idx)
|
||||||
reason.append(reason.empty() ? "" : " ").append(params[idx]);
|
reason.append(reason.empty() ? "" : " ").append(params[idx]);
|
||||||
|
|
||||||
CSSuspendInfo *si = ci->Extend<CSSuspendInfo>("CS_SUSPENDED");
|
auto *si = ci->Extend<CSSuspendInfo>("CS_SUSPENDED");
|
||||||
si->what = ci->name;
|
si->what = ci->name;
|
||||||
si->by = source.GetNick();
|
si->by = source.GetNick();
|
||||||
si->reason = reason;
|
si->reason = reason;
|
||||||
@@ -186,7 +183,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Only UNSUSPEND already suspended channels */
|
/* Only UNSUSPEND already suspended channels */
|
||||||
CSSuspendInfo *si = ci->GetExt<CSSuspendInfo>("CS_SUSPENDED");
|
auto *si = ci->GetExt<CSSuspendInfo>("CS_SUSPENDED");
|
||||||
if (!si)
|
if (!si)
|
||||||
{
|
{
|
||||||
source.Reply(_("Channel \002%s\002 isn't suspended."), ci->name.c_str());
|
source.Reply(_("Channel \002%s\002 isn't suspended."), ci->name.c_str());
|
||||||
@@ -254,7 +251,7 @@ public:
|
|||||||
|
|
||||||
void OnReload(Configuration::Conf &conf) override
|
void OnReload(Configuration::Conf &conf) override
|
||||||
{
|
{
|
||||||
Anope::string s = conf.GetModule(this).Get<Anope::string>("show");
|
auto s = conf.GetModule(this).Get<Anope::string>("show");
|
||||||
commasepstream(s).GetTokens(show);
|
commasepstream(s).GetTokens(show);
|
||||||
std::transform(show.begin(), show.end(), show.begin(), trim());
|
std::transform(show.begin(), show.end(), show.begin(), trim());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,11 +128,12 @@ public:
|
|||||||
"user from entering the given channel. If no channel is "
|
"user from entering the given channel. If no channel is "
|
||||||
"given, all bans affecting you in channels you have access "
|
"given, all bans affecting you in channels you have access "
|
||||||
"in are removed."
|
"in are removed."
|
||||||
"\n\n"
|
|
||||||
"By default, limited to AOPs or those with level 5 access and above "
|
|
||||||
"on the channel."
|
|
||||||
),
|
),
|
||||||
source.service->nick.c_str());
|
source.service->nick.c_str());
|
||||||
|
|
||||||
|
source.Reply(" ");
|
||||||
|
AccessProvider::SendAccess(source, "UNBAN");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (!source.GetUser())
|
if (!source.GetUser())
|
||||||
return;
|
return;
|
||||||
for (User::ChanUserList::iterator it = source.GetUser()->chans.begin(); it != source.GetUser()->chans.end(); ++it)
|
for (auto it = source.GetUser()->chans.begin(); it != source.GetUser()->chans.end(); ++it)
|
||||||
{
|
{
|
||||||
Channel *c = it->second->chan;
|
Channel *c = it->second->chan;
|
||||||
SetModes(source.GetUser(), c);
|
SetModes(source.GetUser(), c);
|
||||||
@@ -162,7 +162,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (!source.GetUser())
|
if (!source.GetUser())
|
||||||
return;
|
return;
|
||||||
for (User::ChanUserList::iterator it = source.GetUser()->chans.begin(); it != source.GetUser()->chans.end(); ++it)
|
for (auto it = source.GetUser()->chans.begin(); it != source.GetUser()->chans.end(); ++it)
|
||||||
{
|
{
|
||||||
Channel *c = it->second->chan;
|
Channel *c = it->second->chan;
|
||||||
RemoveAll(source.GetUser(), c);
|
RemoveAll(source.GetUser(), c);
|
||||||
|
|||||||
+26
-15
@@ -32,7 +32,7 @@ public:
|
|||||||
|
|
||||||
bool HasPriv(const Anope::string &priv) const override
|
bool HasPriv(const Anope::string &priv) const override
|
||||||
{
|
{
|
||||||
for (std::vector<Anope::string>::iterator it = std::find(order.begin(), order.end(), this->type); it != order.end(); ++it)
|
for (auto it = std::find(order.begin(), order.end(), this->type); it != order.end(); ++it)
|
||||||
{
|
{
|
||||||
const std::vector<Anope::string> &privs = permissions[*it];
|
const std::vector<Anope::string> &privs = permissions[*it];
|
||||||
if (std::find(privs.begin(), privs.end(), priv) != privs.end())
|
if (std::find(privs.begin(), privs.end(), priv) != privs.end())
|
||||||
@@ -55,7 +55,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (access->provider->name == "access/xop")
|
if (access->provider->name == "access/xop")
|
||||||
{
|
{
|
||||||
const XOPChanAccess *xaccess = anope_dynamic_static_cast<const XOPChanAccess *>(access);
|
const auto *xaccess = anope_dynamic_static_cast<const XOPChanAccess *>(access);
|
||||||
return xaccess->type;
|
return xaccess->type;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -100,6 +100,19 @@ public:
|
|||||||
{
|
{
|
||||||
return new XOPChanAccess(this);
|
return new XOPChanAccess(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAccess(CommandSource& source, const Privilege *p, Anope::map<Anope::string> &access) override
|
||||||
|
{
|
||||||
|
for (const auto& xop : order)
|
||||||
|
{
|
||||||
|
const auto &privs = permissions[xop];
|
||||||
|
if (std::find(privs.begin(), privs.end(), p->name) != privs.end())
|
||||||
|
{
|
||||||
|
access[_("XOP")] = xop;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandCSXOP final
|
class CommandCSXOP final
|
||||||
@@ -128,7 +141,7 @@ private:
|
|||||||
bool override = false;
|
bool override = false;
|
||||||
const NickAlias *na = NULL;
|
const NickAlias *na = NULL;
|
||||||
|
|
||||||
std::vector<Anope::string>::iterator cmd_it = std::find(order.begin(), order.end(), source.command.upper()),
|
auto cmd_it = std::find(order.begin(), order.end(), source.command.upper()),
|
||||||
access_it = highest ? std::find(order.begin(), order.end(), XOPChanAccess::DetermineLevel(highest)) : order.end();
|
access_it = highest ? std::find(order.begin(), order.end(), XOPChanAccess::DetermineLevel(highest)) : order.end();
|
||||||
|
|
||||||
if (!access.founder && (!access.HasPriv("ACCESS_CHANGE") || cmd_it <= access_it))
|
if (!access.founder && (!access.HasPriv("ACCESS_CHANGE") || cmd_it <= access_it))
|
||||||
@@ -252,7 +265,7 @@ private:
|
|||||||
ServiceReference<AccessProvider> provider("AccessProvider", "access/xop");
|
ServiceReference<AccessProvider> provider("AccessProvider", "access/xop");
|
||||||
if (!provider)
|
if (!provider)
|
||||||
return;
|
return;
|
||||||
XOPChanAccess *acc = anope_dynamic_static_cast<XOPChanAccess *>(provider->Create());
|
auto *acc = anope_dynamic_static_cast<XOPChanAccess *>(provider->Create());
|
||||||
acc->SetMask(mask, ci);
|
acc->SetMask(mask, ci);
|
||||||
acc->creator = source.GetNick();
|
acc->creator = source.GetNick();
|
||||||
acc->description = description;
|
acc->description = description;
|
||||||
@@ -263,7 +276,7 @@ private:
|
|||||||
|
|
||||||
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask;
|
Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask;
|
||||||
|
|
||||||
FOREACH_MOD(OnAccessAdd, (ci, source, acc));
|
FOREACH_MOD(OnAccessAdd, (ci, source, acc, false));
|
||||||
source.Reply(_("\002%s\002 added to %s %s list."), acc->Mask().c_str(), ci->name.c_str(), source.command.nobreak().c_str());
|
source.Reply(_("\002%s\002 added to %s %s list."), acc->Mask().c_str(), ci->name.c_str(), source.command.nobreak().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +324,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Anope::string>::iterator cmd_it = std::find(order.begin(), order.end(), source.command.upper()),
|
auto cmd_it = std::find(order.begin(), order.end(), source.command.upper()),
|
||||||
access_it = highest ? std::find(order.begin(), order.end(), XOPChanAccess::DetermineLevel(highest)) : order.end();
|
access_it = highest ? std::find(order.begin(), order.end(), XOPChanAccess::DetermineLevel(highest)) : order.end();
|
||||||
|
|
||||||
if (!mask.equals_ci(nc->display) && !access.founder && (!access.HasPriv("ACCESS_CHANGE") || cmd_it <= access_it))
|
if (!mask.equals_ci(nc->display) && !access.founder && (!access.HasPriv("ACCESS_CHANGE") || cmd_it <= access_it))
|
||||||
@@ -372,7 +385,7 @@ private:
|
|||||||
nicks += caccess->Mask();
|
nicks += caccess->Mask();
|
||||||
|
|
||||||
ci->EraseAccess(number - 1);
|
ci->EraseAccess(number - 1);
|
||||||
FOREACH_MOD(OnAccessDel, (ci, source, caccess));
|
FOREACH_MOD(OnAccessDel, (ci, source, caccess, false));
|
||||||
delete caccess;
|
delete caccess;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,7 +408,7 @@ private:
|
|||||||
source.Reply(_("\002%s\002 deleted from %s %s list."), a->Mask().c_str(), ci->name.c_str(), source.command.nobreak().c_str());
|
source.Reply(_("\002%s\002 deleted from %s %s list."), a->Mask().c_str(), ci->name.c_str(), source.command.nobreak().c_str());
|
||||||
|
|
||||||
ci->EraseAccess(i);
|
ci->EraseAccess(i);
|
||||||
FOREACH_MOD(OnAccessDel, (ci, source, a));
|
FOREACH_MOD(OnAccessDel, (ci, source, a, false));
|
||||||
delete a;
|
delete a;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -543,7 +556,7 @@ public:
|
|||||||
|
|
||||||
Anope::string GetDesc(CommandSource &source) const override
|
Anope::string GetDesc(CommandSource &source) const override
|
||||||
{
|
{
|
||||||
return Anope::Format(Language::Translate(source.GetAccount(), _("Modify the list of %s users")), source.command.nobreak().c_str());
|
return Anope::Format(source.Translate(_("Modify the list of %s users")), source.command.nobreak().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||||
@@ -636,8 +649,8 @@ public:
|
|||||||
|
|
||||||
BotInfo *access_bi, *flags_bi;
|
BotInfo *access_bi, *flags_bi;
|
||||||
Anope::string access_cmd, flags_cmd;
|
Anope::string access_cmd, flags_cmd;
|
||||||
Command::FindCommandFromService("chanserv/access", access_bi, access_cmd);
|
Command::FindFromService("chanserv/access", access_bi, access_cmd);
|
||||||
Command::FindCommandFromService("chanserv/flags", flags_bi, flags_cmd);
|
Command::FindFromService("chanserv/flags", flags_bi, flags_cmd);
|
||||||
if (!access_cmd.empty() || !flags_cmd.empty())
|
if (!access_cmd.empty() || !flags_cmd.empty())
|
||||||
{
|
{
|
||||||
source.Reply(_("Alternative methods of modifying channel access lists are available."));
|
source.Reply(_("Alternative methods of modifying channel access lists are available."));
|
||||||
@@ -675,9 +688,8 @@ public:
|
|||||||
order.clear();
|
order.clear();
|
||||||
permissions.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");
|
const Anope::string &pname = block.Get<const Anope::string>("name");
|
||||||
|
|
||||||
Privilege *p = PrivilegeManager::FindPrivilege(pname);
|
Privilege *p = PrivilegeManager::FindPrivilege(pname);
|
||||||
@@ -691,9 +703,8 @@ public:
|
|||||||
permissions[xop].push_back(pname);
|
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"),
|
const Anope::string &cname = block.Get<const Anope::string>("name"),
|
||||||
&cserv = block.Get<const Anope::string>("command");
|
&cserv = block.Get<const Anope::string>("command");
|
||||||
if (cname.empty() || cserv != "chanserv/xop")
|
if (cname.empty() || cserv != "chanserv/xop")
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ class MChanstats final
|
|||||||
for (int i = 0; i < r.Rows(); ++i)
|
for (int i = 0; i < r.Rows(); ++i)
|
||||||
{
|
{
|
||||||
const std::map<Anope::string, Anope::string> &map = r.Row(i);
|
const std::map<Anope::string, Anope::string> &map = r.Row(i);
|
||||||
for (std::map<Anope::string, Anope::string>::const_iterator it = map.begin(); it != map.end(); ++it)
|
for (auto it = map.begin(); it != map.end(); ++it)
|
||||||
TableList.push_back(it->second);
|
TableList.push_back(it->second);
|
||||||
}
|
}
|
||||||
query = "SHOW PROCEDURE STATUS WHERE `Db` = Database();";
|
query = "SHOW PROCEDURE STATUS WHERE `Db` = Database();";
|
||||||
@@ -254,7 +254,7 @@ class MChanstats final
|
|||||||
|
|
||||||
bool HasTable(const Anope::string &table)
|
bool HasTable(const Anope::string &table)
|
||||||
{
|
{
|
||||||
for (std::vector<Anope::string>::const_iterator it = TableList.begin(); it != TableList.end(); ++it)
|
for (auto it = TableList.begin(); it != TableList.end(); ++it)
|
||||||
if (*it == table)
|
if (*it == table)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
@@ -262,7 +262,7 @@ class MChanstats final
|
|||||||
|
|
||||||
bool HasProcedure(const Anope::string &table)
|
bool HasProcedure(const Anope::string &table)
|
||||||
{
|
{
|
||||||
for (std::vector<Anope::string>::const_iterator it = ProcedureList.begin(); it != ProcedureList.end(); ++it)
|
for (auto it = ProcedureList.begin(); it != ProcedureList.end(); ++it)
|
||||||
if (*it == table)
|
if (*it == table)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
@@ -270,7 +270,7 @@ class MChanstats final
|
|||||||
|
|
||||||
bool HasEvent(const Anope::string &table)
|
bool HasEvent(const Anope::string &table)
|
||||||
{
|
{
|
||||||
for (std::vector<Anope::string>::const_iterator it = EventList.begin(); it != EventList.end(); ++it)
|
for (auto it = EventList.begin(); it != EventList.end(); ++it)
|
||||||
if (*it == table)
|
if (*it == table)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
@@ -353,14 +353,16 @@ class MChanstats final
|
|||||||
"(nick_, '', 'total'), (nick_, '', 'monthly'),"
|
"(nick_, '', 'total'), (nick_, '', 'monthly'),"
|
||||||
"(nick_, '', 'weekly'), (nick_, '', 'daily');"
|
"(nick_, '', 'weekly'), (nick_, '', 'daily');"
|
||||||
"END IF;"
|
"END IF;"
|
||||||
|
"SET @echan = chan_;"
|
||||||
|
"SET @enick = nick_;"
|
||||||
"SET @update_query = CONCAT('UPDATE `" + prefix + "chanstats` SET line=line+', line_, ',"
|
"SET @update_query = CONCAT('UPDATE `" + prefix + "chanstats` SET line=line+', line_, ',"
|
||||||
"letters=letters+', letters_, ' , words=words+', words_, ', actions=actions+', actions_, ', "
|
"letters=letters+', letters_, ' , words=words+', words_, ', actions=actions+', actions_, ', "
|
||||||
"smileys_happy=smileys_happy+', sm_h_, ', smileys_sad=smileys_sad+', sm_s_, ', "
|
"smileys_happy=smileys_happy+', sm_h_, ', smileys_sad=smileys_sad+', sm_s_, ', "
|
||||||
"smileys_other=smileys_other+', sm_o_, ', kicks=kicks+', kicks_, ', kicked=kicked+', kicked_, ', "
|
"smileys_other=smileys_other+', sm_o_, ', kicks=kicks+', kicks_, ', kicked=kicked+', kicked_, ', "
|
||||||
"modes=modes+', modes_, ', topics=topics+', topics_, ', ', time_ , '=', time_, '+', line_ ,' "
|
"modes=modes+', modes_, ', topics=topics+', topics_, ', ', time_ , '=', time_, '+', line_ ,' "
|
||||||
"WHERE (nick='''' OR nick=''', nick_, ''') AND (chan='''' OR chan=''', chan_, ''')');"
|
"WHERE (nick='''' OR nick=?) AND (chan='''' OR chan=?)');"
|
||||||
"PREPARE update_query FROM @update_query;"
|
"PREPARE update_query FROM @update_query;"
|
||||||
"EXECUTE update_query;"
|
"EXECUTE update_query using @enick, @echan;"
|
||||||
"DEALLOCATE PREPARE update_query;"
|
"DEALLOCATE PREPARE update_query;"
|
||||||
"END";
|
"END";
|
||||||
this->RunQuery(query);
|
this->RunQuery(query);
|
||||||
@@ -509,7 +511,7 @@ public:
|
|||||||
void OnReload(Configuration::Conf &conf) override
|
void OnReload(Configuration::Conf &conf) override
|
||||||
{
|
{
|
||||||
const auto &block = conf.GetModule(this);
|
const auto &block = conf.GetModule(this);
|
||||||
prefix = block.Get<const Anope::string>("prefix", "anope_");
|
prefix = block.Get<const Anope::string>("prefix", "chanstats21_");
|
||||||
SmileysHappy = block.Get<const Anope::string>("SmileysHappy");
|
SmileysHappy = block.Get<const Anope::string>("SmileysHappy");
|
||||||
SmileysSad = block.Get<const Anope::string>("SmileysSad");
|
SmileysSad = block.Get<const Anope::string>("SmileysSad");
|
||||||
SmileysOther = block.Get<const Anope::string>("SmileysOther");
|
SmileysOther = block.Get<const Anope::string>("SmileysOther");
|
||||||
|
|||||||
+176
-25
@@ -12,6 +12,7 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "modules/botserv/badwords.h"
|
#include "modules/botserv/badwords.h"
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
#include "modules/chanserv/mode.h"
|
#include "modules/chanserv/mode.h"
|
||||||
#include "modules/hostserv/request.h"
|
#include "modules/hostserv/request.h"
|
||||||
#include "modules/info.h"
|
#include "modules/info.h"
|
||||||
|
#include "modules/nickserv/ajoin.h"
|
||||||
#include "modules/nickserv/cert.h"
|
#include "modules/nickserv/cert.h"
|
||||||
#include "modules/operserv/forbid.h"
|
#include "modules/operserv/forbid.h"
|
||||||
#include "modules/operserv/news.h"
|
#include "modules/operserv/news.h"
|
||||||
@@ -73,10 +75,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Retrieves the remaining data in the row.
|
// Retrieves the remaining data in the row.
|
||||||
Anope::string GetRemaining()
|
Anope::string GetRemaining(bool allow_empty = false)
|
||||||
{
|
{
|
||||||
auto remaining = stream.GetRemaining();
|
auto remaining = stream.GetRemaining();
|
||||||
if (remaining.empty())
|
if (remaining.empty() && !allow_empty)
|
||||||
error++;
|
error++;
|
||||||
return remaining;
|
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
|
struct ChannelData final
|
||||||
{
|
{
|
||||||
Anope::unordered_map<ChanServ::AutoKick *> akicks;
|
Anope::unordered_map<ChanServ::AutoKick *> akicks;
|
||||||
Anope::string bot;
|
Anope::string bot;
|
||||||
|
FounderSuccessorCandidate founder_candidate;
|
||||||
Anope::string info_adder;
|
Anope::string info_adder;
|
||||||
Anope::string info_message;
|
Anope::string info_message;
|
||||||
time_t info_ts = 0;
|
time_t info_ts = 0;
|
||||||
std::vector<ModeLockData> mlocks;
|
std::vector<ModeLockData> mlocks;
|
||||||
|
FounderSuccessorCandidate successor_candidate;
|
||||||
Anope::string suspend_by;
|
Anope::string suspend_by;
|
||||||
Anope::string suspend_reason;
|
Anope::string suspend_reason;
|
||||||
time_t suspend_ts = 0;
|
time_t suspend_ts = 0;
|
||||||
@@ -135,6 +172,7 @@ struct ChannelData final
|
|||||||
|
|
||||||
struct UserData final
|
struct UserData final
|
||||||
{
|
{
|
||||||
|
Anope::map<Anope::string> ajoins;
|
||||||
Anope::string info_adder;
|
Anope::string info_adder;
|
||||||
Anope::string info_message;
|
Anope::string info_message;
|
||||||
time_t info_ts = 0;
|
time_t info_ts = 0;
|
||||||
@@ -236,24 +274,43 @@ private:
|
|||||||
{ "XL", &DBAtheme::HandleXL },
|
{ "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);
|
in.erase(pos, 1);
|
||||||
if (pos != Anope::string::npos)
|
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);
|
auto privchar = flags.find(priv);
|
||||||
if (privchar != flags.end())
|
if (privchar != flags.end())
|
||||||
{
|
{
|
||||||
out.push_back(privchar->second);
|
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)
|
void ApplyFlags(Extensible *ext, Anope::string &flags, char flag, const char *extname, bool extend = true)
|
||||||
{
|
{
|
||||||
auto pos = flags.find(flag);
|
auto pos = flags.find(flag);
|
||||||
auto has_flag = (pos != Anope::string::npos);
|
auto has_flag = (pos != Anope::string::npos);
|
||||||
@@ -609,16 +666,17 @@ private:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto *data = chandata.Require(ci);
|
||||||
|
|
||||||
auto *nc = NickCore::Find(mask);
|
auto *nc = NickCore::Find(mask);
|
||||||
if (flags.find('b') != Anope::string::npos)
|
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";
|
Log(this) << "Unable to import channel akick for " << ci->name << " as cs_akick is not loaded";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *data = chandata.Require(ci);
|
|
||||||
if (nc)
|
if (nc)
|
||||||
data->akicks[mask] = ChanServ::akick_service->AddAKick(ci, setter, nc, "", modifiedtime, modifiedtime);
|
data->akicks[mask] = ChanServ::akick_service->AddAKick(ci, setter, nc, "", modifiedtime, modifiedtime);
|
||||||
else
|
else
|
||||||
@@ -638,7 +696,6 @@ private:
|
|||||||
ApplyAccess(flags, 'a', accessflags, { "AUTOPROTECT", "PROTECT", "PROTECTME" });
|
ApplyAccess(flags, 'a', accessflags, { "AUTOPROTECT", "PROTECT", "PROTECTME" });
|
||||||
ApplyAccess(flags, 'e', accessflags, { "GETKEY", "NOKICK", "UNBANME" });
|
ApplyAccess(flags, 'e', accessflags, { "GETKEY", "NOKICK", "UNBANME" });
|
||||||
ApplyAccess(flags, 'f', accessflags, { "ACCESS_CHANGE" });
|
ApplyAccess(flags, 'f', accessflags, { "ACCESS_CHANGE" });
|
||||||
ApplyAccess(flags, 'F', accessflags, { "FOUNDER" });
|
|
||||||
ApplyAccess(flags, 'H', accessflags, { "AUTOHALFOP" });
|
ApplyAccess(flags, 'H', accessflags, { "AUTOHALFOP" });
|
||||||
ApplyAccess(flags, 'h', accessflags, { "HALFOP", "HALFOPME" });
|
ApplyAccess(flags, 'h', accessflags, { "HALFOP", "HALFOPME" });
|
||||||
ApplyAccess(flags, 'i', accessflags, { "INVITE" });
|
ApplyAccess(flags, 'i', accessflags, { "INVITE" });
|
||||||
@@ -662,6 +719,35 @@ private:
|
|||||||
ci->AddAccess(access);
|
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 != "+")
|
if (flags != "+")
|
||||||
Log(this) << "Unable to convert channel access flags " << flags << " for " << mask << " on " << ci->name;
|
Log(this) << "Unable to convert channel access flags " << flags << " for " << mask << " on " << ci->name;
|
||||||
|
|
||||||
@@ -788,7 +874,7 @@ private:
|
|||||||
return true;
|
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;
|
xl->id = id;
|
||||||
sglinemgr->AddXLine(xl);
|
sglinemgr->AddXLine(xl);
|
||||||
return true;
|
return true;
|
||||||
@@ -879,6 +965,7 @@ private:
|
|||||||
ci->last_used = used;
|
ci->last_used = used;
|
||||||
|
|
||||||
// No equivalent: elnv
|
// No equivalent: elnv
|
||||||
|
RemoveFirstOccurrence(flags, 'v'); // verbose, com
|
||||||
ApplyFlags(ci, flags, 'h', "CS_NO_EXPIRE");
|
ApplyFlags(ci, flags, 'h', "CS_NO_EXPIRE");
|
||||||
ApplyFlags(ci, flags, 'k', "KEEPTOPIC");
|
ApplyFlags(ci, flags, 'k', "KEEPTOPIC");
|
||||||
ApplyFlags(ci, flags, 'o', "NOAUTOOP");
|
ApplyFlags(ci, flags, 'o', "NOAUTOOP");
|
||||||
@@ -985,6 +1072,10 @@ private:
|
|||||||
if (akick != data->akicks.end())
|
if (akick != data->akicks.end())
|
||||||
akick->second->reason = value;
|
akick->second->reason = value;
|
||||||
}
|
}
|
||||||
|
else if (key == "expires")
|
||||||
|
{
|
||||||
|
Log(this) << "Unable to set access expiration for " << mask << " on " << ci->name << ": unimplemented";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
Log(this) << "Unknown channel access metadata for " << mask << " on " << ci->name << ": " << key << " = " << value;
|
Log(this) << "Unknown channel access metadata for " << mask << " on " << ci->name << ": " << key << " = " << value;
|
||||||
|
|
||||||
@@ -1146,10 +1237,7 @@ private:
|
|||||||
// MDU <display> <key> <value>
|
// MDU <display> <key> <value>
|
||||||
auto display = row.Get();
|
auto display = row.Get();
|
||||||
auto key = row.Get();
|
auto key = row.Get();
|
||||||
auto value = row.GetRemaining();
|
auto value = row.GetRemaining(true);
|
||||||
|
|
||||||
if (!row)
|
|
||||||
return row.LogError(this);
|
|
||||||
|
|
||||||
auto *nc = NickCore::Find(display);
|
auto *nc = NickCore::Find(display);
|
||||||
if (!nc)
|
if (!nc)
|
||||||
@@ -1160,7 +1248,17 @@ private:
|
|||||||
|
|
||||||
auto *data = userdata.Require(nc);
|
auto *data = userdata.Require(nc);
|
||||||
if (key == "private:autojoin")
|
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")
|
else if (key == "private:doenforce")
|
||||||
data->protect = true;
|
data->protect = true;
|
||||||
else if (key == "private:enforcetime")
|
else if (key == "private:enforcetime")
|
||||||
@@ -1189,6 +1287,12 @@ private:
|
|||||||
data->info_adder = value;
|
data->info_adder = value;
|
||||||
else if (key == "private:mark:timestamp")
|
else if (key == "private:mark:timestamp")
|
||||||
data->info_ts = Anope::Convert<time_t>(value, 0);
|
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")
|
else if (key == "private:swhois")
|
||||||
return HandleIgnoreMetadata(nc->display, key, value);
|
return HandleIgnoreMetadata(nc->display, key, value);
|
||||||
else if (key == "private:usercloak")
|
else if (key == "private:usercloak")
|
||||||
@@ -1410,6 +1514,7 @@ private:
|
|||||||
ApplyPassword(nc, flags, pass);
|
ApplyPassword(nc, flags, pass);
|
||||||
|
|
||||||
// No equivalent: bglmNQrS
|
// No equivalent: bglmNQrS
|
||||||
|
RemoveFirstOccurrence(flags, 'b'); // nick b flag is ephemeral, ignore
|
||||||
ApplyFlags(nc, flags, 'E', "PROTECT");
|
ApplyFlags(nc, flags, 'E', "PROTECT");
|
||||||
ApplyFlags(nc, flags, 'e', "MEMO_MAIL");
|
ApplyFlags(nc, flags, 'e', "MEMO_MAIL");
|
||||||
ApplyFlags(nc, flags, 'n', "NEVEROP");
|
ApplyFlags(nc, flags, 'n', "NEVEROP");
|
||||||
@@ -1499,7 +1604,7 @@ private:
|
|||||||
return true;
|
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;
|
xl->id = id;
|
||||||
sqlinemgr->AddXLine(xl);
|
sqlinemgr->AddXLine(xl);
|
||||||
return true;
|
return true;
|
||||||
@@ -1561,7 +1666,7 @@ private:
|
|||||||
return true;
|
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;
|
xl->id = id;
|
||||||
snlinemgr->AddXLine(xl);
|
snlinemgr->AddXLine(xl);
|
||||||
return true;
|
return true;
|
||||||
@@ -1584,9 +1689,8 @@ public:
|
|||||||
const auto &modconf = conf.GetModule(this);
|
const auto &modconf = conf.GetModule(this);
|
||||||
|
|
||||||
csmiscdata.clear();
|
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 &anope = data.Get<const Anope::string>("anope");
|
||||||
const auto &atheme = data.Get<const Anope::string>("atheme");
|
const auto &atheme = data.Get<const Anope::string>("atheme");
|
||||||
if (!anope.empty() && !atheme.empty())
|
if (!anope.empty() && !atheme.empty())
|
||||||
@@ -1594,9 +1698,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsmiscdata.clear();
|
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 &anope = data.Get<const Anope::string>("anope");
|
||||||
const auto &atheme = data.Get<const Anope::string>("atheme");
|
const auto &atheme = data.Get<const Anope::string>("atheme");
|
||||||
if (!anope.empty() && !atheme.empty())
|
if (!anope.empty() && !atheme.empty())
|
||||||
@@ -1604,9 +1707,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
flags.clear();
|
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 &name = priv.Get<const Anope::string>("name");
|
||||||
const Anope::string &value = priv.Get<const Anope::string>("flag");
|
const Anope::string &value = priv.Get<const Anope::string>("flag");
|
||||||
if (!name.empty() && !value.empty())
|
if (!name.empty() && !value.empty())
|
||||||
@@ -1692,6 +1794,55 @@ public:
|
|||||||
if (!data)
|
if (!data)
|
||||||
continue;
|
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())
|
if (!data->info_message.empty())
|
||||||
{
|
{
|
||||||
auto *oil = nc->Require<OperInfoList>("operinfo");
|
auto *oil = nc->Require<OperInfoList>("operinfo");
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ public:
|
|||||||
std::fstream *fs;
|
std::fstream *fs;
|
||||||
Serializable::Id id = 0;
|
Serializable::Id id = 0;
|
||||||
std::map<Anope::string, Anope::string> data;
|
std::map<Anope::string, Anope::string> data;
|
||||||
std::stringstream ss;
|
|
||||||
bool read = false;
|
bool read = false;
|
||||||
|
|
||||||
LoadData(std::fstream &fsref)
|
LoadData(std::fstream &fsref)
|
||||||
@@ -29,7 +28,7 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::iostream &operator[](const Anope::string &key) override
|
bool LoadInternal(const Anope::string &key, Anope::string &value) override
|
||||||
{
|
{
|
||||||
if (!read)
|
if (!read)
|
||||||
{
|
{
|
||||||
@@ -51,9 +50,13 @@ public:
|
|||||||
read = true;
|
read = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ss.clear();
|
value = this->data[key];
|
||||||
this->ss << this->data[key];
|
return true;
|
||||||
return this->ss;
|
}
|
||||||
|
|
||||||
|
bool StoreInternal(const Anope::string &key, const Anope::string &value) override
|
||||||
|
{
|
||||||
|
return false; // This module can only load data.
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Hash() const override
|
size_t Hash() const override
|
||||||
|
|||||||
@@ -12,10 +12,14 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
/// BEGIN CMAKE
|
||||||
|
/// target_link_libraries(${SO} PRIVATE "vendored_yyjson")
|
||||||
|
/// END CMAKE
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
#include "yyjson/yyjson.c"
|
#include "yyjson/yyjson.h"
|
||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
|
|
||||||
@@ -38,7 +42,7 @@ public:
|
|||||||
Serializable::Id id = 0;
|
Serializable::Id id = 0;
|
||||||
|
|
||||||
// Data in this database entry.
|
// Data in this database entry.
|
||||||
Anope::map<std::stringstream> data;
|
Anope::unordered_map<Anope::string> data;
|
||||||
|
|
||||||
// Used when writing data.
|
// Used when writing data.
|
||||||
Data(Serialize::Type *s_type, Serializable *obj)
|
Data(Serialize::Type *s_type, Serializable *obj)
|
||||||
@@ -58,7 +62,7 @@ public:
|
|||||||
if (yyjson_get_type(key) != YYJSON_TYPE_STR)
|
if (yyjson_get_type(key) != YYJSON_TYPE_STR)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto akey = yyjson_get_astr(key);
|
const auto akey = yyjson_get_astr(key);
|
||||||
if (akey.equals_ci("@id"))
|
if (akey.equals_ci("@id"))
|
||||||
{
|
{
|
||||||
this->id = yyjson_get_uint(value);
|
this->id = yyjson_get_uint(value);
|
||||||
@@ -66,23 +70,28 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (yyjson_is_bool(value))
|
if (yyjson_is_bool(value))
|
||||||
data[akey] << yyjson_get_bool(value);
|
data[akey] = Anope::ToString(yyjson_get_bool(value));
|
||||||
else if (yyjson_is_int(value))
|
else if (yyjson_is_int(value))
|
||||||
data[akey] << yyjson_get_int(value);
|
data[akey] = Anope::ToString(yyjson_get_int(value));
|
||||||
else if (yyjson_is_null(value))
|
else if (yyjson_is_null(value))
|
||||||
data[akey];
|
data[akey];
|
||||||
else if (yyjson_is_real(value))
|
else if (yyjson_is_real(value))
|
||||||
data[akey] << yyjson_get_real(value);
|
data[akey] = Anope::ToString(yyjson_get_real(value));
|
||||||
else if (yyjson_is_str(value))
|
else if (yyjson_is_str(value))
|
||||||
data[akey] << yyjson_get_astr(value);
|
data[akey] = Anope::ToString(yyjson_get_astr(value));
|
||||||
else if (yyjson_is_uint(value))
|
else if (yyjson_is_uint(value))
|
||||||
data[akey] << yyjson_get_uint(value);
|
data[akey] = Anope::ToString(yyjson_get_uint(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::iostream &operator[](const Anope::string &key) override
|
bool LoadInternal(const Anope::string &key, Anope::string &value) override
|
||||||
{
|
{
|
||||||
return data[key];
|
auto it = this->data.find(key);
|
||||||
|
if (it == this->data.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
value = it->second;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Hash() const override
|
size_t Hash() const override
|
||||||
@@ -96,6 +105,12 @@ public:
|
|||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StoreInternal(const Anope::string &key, const Anope::string &value) override
|
||||||
|
{
|
||||||
|
this->data[key] = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DBJSON final
|
class DBJSON final
|
||||||
@@ -318,22 +333,23 @@ private:
|
|||||||
switch (data.GetType(key))
|
switch (data.GetType(key))
|
||||||
{
|
{
|
||||||
case Serialize::DataType::BOOL:
|
case Serialize::DataType::BOOL:
|
||||||
v = yyjson_mut_bool(doc, Anope::Convert<bool>(value.str(), false));
|
v = yyjson_mut_bool(doc, Anope::Convert<bool>(value, false));
|
||||||
break;
|
break;
|
||||||
case Serialize::DataType::FLOAT:
|
case Serialize::DataType::FLOAT:
|
||||||
v = yyjson_mut_real(doc, Anope::Convert<double>(value.str(), 0.0));
|
v = yyjson_mut_real(doc, Anope::Convert<double>(value, 0.0));
|
||||||
break;
|
break;
|
||||||
case Serialize::DataType::INT:
|
case Serialize::DataType::INT:
|
||||||
v = yyjson_mut_int(doc, Anope::Convert<int64_t>(value.str(), 0));
|
v = yyjson_mut_int(doc, Anope::Convert<int64_t>(value, 0));
|
||||||
break;
|
break;
|
||||||
case Serialize::DataType::TEXT:
|
case Serialize::DataType::TEXT:
|
||||||
{
|
{
|
||||||
auto str = value.str();
|
v = value.empty()
|
||||||
v = str.empty() ? yyjson_mut_null(doc) : yyjson_mut_strncpy(doc, str.c_str(), str.length());
|
? yyjson_mut_null(doc)
|
||||||
|
: yyjson_mut_strncpy(doc, value.c_str(), value.length());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Serialize::DataType::UINT:
|
case Serialize::DataType::UINT:
|
||||||
v = yyjson_mut_uint(doc, Anope::Convert<uint64_t>(value.str(), 0));
|
v = yyjson_mut_uint(doc, Anope::Convert<uint64_t>(value, 0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,9 +416,18 @@ public:
|
|||||||
// Step 2: store the new data.
|
// Step 2: store the new data.
|
||||||
for (auto *item : Serializable::GetItems())
|
for (auto *item : Serializable::GetItems())
|
||||||
{
|
{
|
||||||
|
if (!item->ShouldCommit())
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG) << "Not committing dead " << item->GetSerializableName() << " object: " << item;
|
||||||
|
continue; // Non-committable object.
|
||||||
|
}
|
||||||
|
|
||||||
auto *s_type = item->GetSerializableType();
|
auto *s_type = item->GetSerializableType();
|
||||||
if (!s_type)
|
if (!s_type)
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG) << "Not committing orphaned " << item->GetSerializableName() << " object: " << item;
|
||||||
continue; // Provider has been unloaded.
|
continue; // Provider has been unloaded.
|
||||||
|
}
|
||||||
|
|
||||||
// This should always be found because we create it in the previous step.
|
// This should always be found because we create it in the previous step.
|
||||||
auto it = databases.find(s_type->GetOwner());
|
auto it = databases.find(s_type->GetOwner());
|
||||||
|
|||||||
@@ -1,646 +0,0 @@
|
|||||||
// 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
|
|
||||||
|
|
||||||
#include "module.h"
|
|
||||||
#include "modules/redis.h"
|
|
||||||
|
|
||||||
using namespace Redis;
|
|
||||||
|
|
||||||
class DatabaseRedis;
|
|
||||||
static DatabaseRedis *me;
|
|
||||||
|
|
||||||
class Data final
|
|
||||||
: public Serialize::Data
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::map<Anope::string, std::stringstream *> data;
|
|
||||||
|
|
||||||
~Data() override
|
|
||||||
{
|
|
||||||
for (auto &[_, stream] : data)
|
|
||||||
delete stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::iostream &operator[](const Anope::string &key) override
|
|
||||||
{
|
|
||||||
std::stringstream *&stream = data[key];
|
|
||||||
if (!stream)
|
|
||||||
stream = new std::stringstream();
|
|
||||||
return *stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Hash() const override
|
|
||||||
{
|
|
||||||
size_t hash = 0;
|
|
||||||
for (const auto &[_, value] : this->data)
|
|
||||||
if (!value->str().empty())
|
|
||||||
hash ^= Anope::hash_cs()(value->str());
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TypeLoader final
|
|
||||||
: public Interface
|
|
||||||
{
|
|
||||||
Anope::string type;
|
|
||||||
public:
|
|
||||||
TypeLoader(Module *creator, const Anope::string &t) : Interface(creator), type(t) { }
|
|
||||||
|
|
||||||
void OnResult(const Reply &r) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ObjectLoader final
|
|
||||||
: public Interface
|
|
||||||
{
|
|
||||||
Anope::string type;
|
|
||||||
int64_t id;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ObjectLoader(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { }
|
|
||||||
|
|
||||||
void OnResult(const Reply &r) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IDInterface final
|
|
||||||
: public Interface
|
|
||||||
{
|
|
||||||
Reference<Serializable> o;
|
|
||||||
public:
|
|
||||||
IDInterface(Module *creator, Serializable *obj) : Interface(creator), o(obj) { }
|
|
||||||
|
|
||||||
void OnResult(const Reply &r) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Deleter final
|
|
||||||
: public Interface
|
|
||||||
{
|
|
||||||
Anope::string type;
|
|
||||||
int64_t id;
|
|
||||||
public:
|
|
||||||
Deleter(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { }
|
|
||||||
|
|
||||||
void OnResult(const Reply &r) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Updater final
|
|
||||||
: public Interface
|
|
||||||
{
|
|
||||||
Anope::string type;
|
|
||||||
int64_t id;
|
|
||||||
public:
|
|
||||||
Updater(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { }
|
|
||||||
|
|
||||||
void OnResult(const Reply &r) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ModifiedObject final
|
|
||||||
: public Interface
|
|
||||||
{
|
|
||||||
Anope::string type;
|
|
||||||
int64_t id;
|
|
||||||
public:
|
|
||||||
ModifiedObject(Module *creator, const Anope::string &t, int64_t i) : Interface(creator), type(t), id(i) { }
|
|
||||||
|
|
||||||
void OnResult(const Reply &r) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SubscriptionListener final
|
|
||||||
: public Interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SubscriptionListener(Module *creator) : Interface(creator) { }
|
|
||||||
|
|
||||||
void OnResult(const Reply &r) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DatabaseRedis final
|
|
||||||
: public Module
|
|
||||||
, public Pipe
|
|
||||||
{
|
|
||||||
SubscriptionListener sl;
|
|
||||||
std::set<Serializable *> updated_items;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ServiceReference<Provider> redis;
|
|
||||||
|
|
||||||
DatabaseRedis(const Anope::string &modname, const Anope::string &creator)
|
|
||||||
: Module(modname, creator, DATABASE | VENDOR)
|
|
||||||
, sl(this)
|
|
||||||
, redis("Redis::Provider")
|
|
||||||
{
|
|
||||||
me = this;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert or update an object */
|
|
||||||
void InsertObject(Serializable *obj)
|
|
||||||
{
|
|
||||||
Serialize::Type *t = obj->GetSerializableType();
|
|
||||||
|
|
||||||
/* If there is no id yet for this object, get one */
|
|
||||||
if (!obj->object_id)
|
|
||||||
redis->SendCommand(new IDInterface(this, obj), "INCR id:" + t->GetName());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Data data;
|
|
||||||
t->Serialize(obj, data);
|
|
||||||
|
|
||||||
if (obj->IsCached(data))
|
|
||||||
return;
|
|
||||||
|
|
||||||
obj->UpdateCache(data);
|
|
||||||
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("HGETALL");
|
|
||||||
args.push_back("hash:" + t->GetName() + ":" + Anope::ToString(obj->object_id));
|
|
||||||
|
|
||||||
/* Get object attrs to clear before updating */
|
|
||||||
redis->SendCommand(new Updater(this, t->GetName(), obj->object_id), args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnNotify() override
|
|
||||||
{
|
|
||||||
for (auto *obj : this->updated_items)
|
|
||||||
{
|
|
||||||
this->InsertObject(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->updated_items.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnReload(Configuration::Conf &conf) override
|
|
||||||
{
|
|
||||||
const auto &block = conf.GetModule(this);
|
|
||||||
this->redis.SetServiceName(block.Get<const Anope::string>("engine", "redis/main"));
|
|
||||||
}
|
|
||||||
|
|
||||||
EventReturn OnLoadDatabase() override
|
|
||||||
{
|
|
||||||
if (!redis)
|
|
||||||
{
|
|
||||||
Log(this) << "Unable to load database - unable to find redis provider";
|
|
||||||
return EVENT_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &type_order : Serialize::Type::GetTypeOrder())
|
|
||||||
{
|
|
||||||
Serialize::Type *sb = Serialize::Type::Find(type_order);
|
|
||||||
this->OnSerializeTypeCreate(sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!redis->IsSocketDead() && redis->BlockAndProcess());
|
|
||||||
|
|
||||||
if (redis->IsSocketDead())
|
|
||||||
{
|
|
||||||
Log(this) << "I/O error while loading redis database - is it online?";
|
|
||||||
return EVENT_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
redis->Subscribe(&this->sl, "__keyspace@*__:hash:*");
|
|
||||||
|
|
||||||
return EVENT_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnSerializeTypeCreate(Serialize::Type *sb) override
|
|
||||||
{
|
|
||||||
if (!redis)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("SMEMBERS");
|
|
||||||
args.push_back("ids:" + sb->GetName());
|
|
||||||
|
|
||||||
redis->SendCommand(new TypeLoader(this, sb->GetName()), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnSerializableConstruct(Serializable *obj) override
|
|
||||||
{
|
|
||||||
this->updated_items.insert(obj);
|
|
||||||
this->Notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnSerializableDestruct(Serializable *obj) override
|
|
||||||
{
|
|
||||||
Serialize::Type *t = obj->GetSerializableType();
|
|
||||||
|
|
||||||
if (t == NULL)
|
|
||||||
{
|
|
||||||
/* This is probably the module providing the type unloading.
|
|
||||||
*
|
|
||||||
* The types get registered after the extensible container is
|
|
||||||
* registered so that unserialization on module load can insert
|
|
||||||
* into the extensible container. So, the type destructs prior to
|
|
||||||
* the extensible container, which then triggers this
|
|
||||||
*/
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("HGETALL");
|
|
||||||
args.push_back("hash:" + t->GetName() + ":" + Anope::ToString(obj->object_id));
|
|
||||||
|
|
||||||
/* Get all of the attributes for this object */
|
|
||||||
redis->SendCommand(new Deleter(this, t->GetName(), obj->object_id), args);
|
|
||||||
|
|
||||||
this->updated_items.erase(obj);
|
|
||||||
t->objects.erase(obj->object_id);
|
|
||||||
this->Notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnSerializableUpdate(Serializable *obj) override
|
|
||||||
{
|
|
||||||
this->updated_items.insert(obj);
|
|
||||||
this->Notify();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void TypeLoader::OnResult(const Reply &r)
|
|
||||||
{
|
|
||||||
if (r.type != Reply::MULTI_BULK || !me->redis)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto *reply : r.multi_bulk)
|
|
||||||
{
|
|
||||||
if (reply->type != Reply::BULK)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto i = Anope::TryConvert<int64_t>(reply->bulk);
|
|
||||||
if (!i)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto id = i.value();
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("HGETALL");
|
|
||||||
args.push_back("hash:" + this->type + ":" + Anope::ToString(id));
|
|
||||||
|
|
||||||
me->redis->SendCommand(new ObjectLoader(me, this->type, id), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ObjectLoader::OnResult(const Reply &r)
|
|
||||||
{
|
|
||||||
Serialize::Type *st = Serialize::Type::Find(this->type);
|
|
||||||
|
|
||||||
if (r.type != Reply::MULTI_BULK || r.multi_bulk.empty() || !me->redis || !st)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Data data;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i + 1 < r.multi_bulk.size(); i += 2)
|
|
||||||
{
|
|
||||||
const Reply *key = r.multi_bulk[i],
|
|
||||||
*value = r.multi_bulk[i + 1];
|
|
||||||
|
|
||||||
data[key->bulk] << value->bulk;
|
|
||||||
}
|
|
||||||
|
|
||||||
Serializable *&obj = st->objects[this->id];
|
|
||||||
obj = st->Unserialize(obj, data);
|
|
||||||
if (obj)
|
|
||||||
{
|
|
||||||
obj->object_id = this->id;
|
|
||||||
obj->UpdateCache(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDInterface::OnResult(const Reply &r)
|
|
||||||
{
|
|
||||||
if (!o || r.type != Reply::INT || !r.i)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Serializable *&obj = o->GetSerializableType()->objects[r.i];
|
|
||||||
if (obj)
|
|
||||||
/* This shouldn't be possible */
|
|
||||||
obj->object_id = 0;
|
|
||||||
|
|
||||||
o->object_id = r.i;
|
|
||||||
obj = o;
|
|
||||||
|
|
||||||
/* Now that we have the id, insert this object for real */
|
|
||||||
anope_dynamic_static_cast<DatabaseRedis *>(this->owner)->InsertObject(o);
|
|
||||||
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Deleter::OnResult(const Reply &r)
|
|
||||||
{
|
|
||||||
if (r.type != Reply::MULTI_BULK || !me->redis || r.multi_bulk.empty())
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Transaction start */
|
|
||||||
me->redis->StartTransaction();
|
|
||||||
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("DEL");
|
|
||||||
args.push_back("hash:" + this->type + ":" + Anope::ToString(this->id));
|
|
||||||
|
|
||||||
/* Delete hash object */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
|
|
||||||
args.clear();
|
|
||||||
args.emplace_back("SREM");
|
|
||||||
args.push_back("ids:" + this->type);
|
|
||||||
args.push_back(Anope::ToString(this->id));
|
|
||||||
|
|
||||||
/* Delete id from ids set */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
|
|
||||||
for (unsigned i = 0; i + 1 < r.multi_bulk.size(); i += 2)
|
|
||||||
{
|
|
||||||
const Reply *key = r.multi_bulk[i],
|
|
||||||
*value = r.multi_bulk[i + 1];
|
|
||||||
|
|
||||||
args.clear();
|
|
||||||
args.emplace_back("SREM");
|
|
||||||
args.push_back("value:" + this->type + ":" + key->bulk + ":" + value->bulk);
|
|
||||||
args.push_back(Anope::ToString(this->id));
|
|
||||||
|
|
||||||
/* Delete value -> object id */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Transaction end */
|
|
||||||
me->redis->CommitTransaction();
|
|
||||||
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Updater::OnResult(const Reply &r)
|
|
||||||
{
|
|
||||||
Serialize::Type *st = Serialize::Type::Find(this->type);
|
|
||||||
|
|
||||||
if (!st)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Serializable *obj = st->objects[this->id];
|
|
||||||
if (!obj)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Data data;
|
|
||||||
st->Serialize(obj, data);
|
|
||||||
|
|
||||||
/* Transaction start */
|
|
||||||
me->redis->StartTransaction();
|
|
||||||
|
|
||||||
for (unsigned i = 0; i + 1 < r.multi_bulk.size(); i += 2)
|
|
||||||
{
|
|
||||||
const Reply *key = r.multi_bulk[i],
|
|
||||||
*value = r.multi_bulk[i + 1];
|
|
||||||
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("SREM");
|
|
||||||
args.push_back("value:" + this->type + ":" + key->bulk + ":" + value->bulk);
|
|
||||||
args.push_back(Anope::ToString(this->id));
|
|
||||||
|
|
||||||
/* Delete value -> object id */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add object id to id set for this type */
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("SADD");
|
|
||||||
args.push_back("ids:" + this->type);
|
|
||||||
args.push_back(Anope::ToString(obj->object_id));
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
|
|
||||||
args.clear();
|
|
||||||
args.emplace_back("HMSET");
|
|
||||||
args.push_back("hash:" + this->type + ":" + Anope::ToString(obj->object_id));
|
|
||||||
|
|
||||||
for (const auto &[key, value] : data.data)
|
|
||||||
{
|
|
||||||
args.push_back(key);
|
|
||||||
args.emplace_back(value->str());
|
|
||||||
|
|
||||||
std::vector<Anope::string> args2;
|
|
||||||
|
|
||||||
args2.emplace_back("SADD");
|
|
||||||
args2.push_back("value:" + this->type + ":" + key + ":" + value->str());
|
|
||||||
args2.push_back(Anope::ToString(obj->object_id));
|
|
||||||
|
|
||||||
/* Add to value -> object id set */
|
|
||||||
me->redis->SendCommand(NULL, args2);
|
|
||||||
}
|
|
||||||
|
|
||||||
++obj->redis_ignore;
|
|
||||||
|
|
||||||
/* Add object */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
|
|
||||||
/* Transaction end */
|
|
||||||
me->redis->CommitTransaction();
|
|
||||||
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SubscriptionListener::OnResult(const Reply &r)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* [May 15 13:59:35.645839 2013] Debug: pmessage
|
|
||||||
* [May 15 13:59:35.645866 2013] Debug: __keyspace@*__:anope:hash:*
|
|
||||||
* [May 15 13:59:35.645880 2013] Debug: __keyspace@0__:anope:hash:type:id
|
|
||||||
* [May 15 13:59:35.645893 2013] Debug: hset
|
|
||||||
*/
|
|
||||||
if (r.multi_bulk.size() != 4)
|
|
||||||
return;
|
|
||||||
|
|
||||||
size_t sz = r.multi_bulk[2]->bulk.find(':');
|
|
||||||
if (sz == Anope::string::npos)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const Anope::string &key = r.multi_bulk[2]->bulk.substr(sz + 1),
|
|
||||||
&op = r.multi_bulk[3]->bulk;
|
|
||||||
|
|
||||||
sz = key.rfind(':');
|
|
||||||
if (sz == Anope::string::npos)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const Anope::string &id = key.substr(sz + 1);
|
|
||||||
|
|
||||||
size_t sz2 = key.rfind(':', sz - 1);
|
|
||||||
if (sz2 == Anope::string::npos)
|
|
||||||
return;
|
|
||||||
const Anope::string &type = key.substr(sz2 + 1, sz - sz2 - 1);
|
|
||||||
|
|
||||||
Serialize::Type *s_type = Serialize::Type::Find(type);
|
|
||||||
|
|
||||||
if (s_type == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto oid = Anope::TryConvert<Serializable::Id>(id);
|
|
||||||
if (!oid.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto obj_id = oid.value();
|
|
||||||
if (op == "hset" || op == "hdel")
|
|
||||||
{
|
|
||||||
Serializable *s = s_type->objects[obj_id];
|
|
||||||
|
|
||||||
if (s && s->redis_ignore)
|
|
||||||
{
|
|
||||||
--s->redis_ignore;
|
|
||||||
Log(LOG_DEBUG) << "redis: notify: got modify for object id " << obj_id << " of type " << type << ", but I am ignoring it";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG) << "redis: notify: got modify for object id " << obj_id << " of type " << type;
|
|
||||||
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("HGETALL");
|
|
||||||
args.push_back("hash:" + type + ":" + id);
|
|
||||||
|
|
||||||
me->redis->SendCommand(new ModifiedObject(me, type, obj_id), args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (op == "del")
|
|
||||||
{
|
|
||||||
Serializable *&s = s_type->objects[obj_id];
|
|
||||||
if (s == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Log(LOG_DEBUG) << "redis: notify: deleting object id " << obj_id << " of type " << type;
|
|
||||||
|
|
||||||
Data data;
|
|
||||||
s_type->Serialize(s, data);
|
|
||||||
|
|
||||||
/* Transaction start */
|
|
||||||
me->redis->StartTransaction();
|
|
||||||
|
|
||||||
for (const auto &[k, value] : data.data)
|
|
||||||
{
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("SREM");
|
|
||||||
args.push_back("value:" + type + ":" + k + ":" + value->str());
|
|
||||||
args.push_back(id);
|
|
||||||
|
|
||||||
/* Delete value -> object id */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("SREM");
|
|
||||||
args.push_back("ids:" + type);
|
|
||||||
args.push_back(Anope::ToString(s->object_id));
|
|
||||||
|
|
||||||
/* Delete object from id set */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
|
|
||||||
/* Transaction end */
|
|
||||||
me->redis->CommitTransaction();
|
|
||||||
|
|
||||||
delete s;
|
|
||||||
s = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModifiedObject::OnResult(const Reply &r)
|
|
||||||
{
|
|
||||||
Serialize::Type *st = Serialize::Type::Find(this->type);
|
|
||||||
|
|
||||||
if (!st)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Serializable *&obj = st->objects[this->id];
|
|
||||||
|
|
||||||
/* Transaction start */
|
|
||||||
me->redis->StartTransaction();
|
|
||||||
|
|
||||||
/* Erase old object values */
|
|
||||||
if (obj)
|
|
||||||
{
|
|
||||||
Data data;
|
|
||||||
st->Serialize(obj, data);
|
|
||||||
|
|
||||||
for (auto &[key, value] : data.data)
|
|
||||||
{
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("SREM");
|
|
||||||
args.push_back("value:" + st->GetName() + ":" + key + ":" + value->str());
|
|
||||||
args.push_back(Anope::ToString(this->id));
|
|
||||||
|
|
||||||
/* Delete value -> object id */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Data data;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i + 1 < r.multi_bulk.size(); i += 2)
|
|
||||||
{
|
|
||||||
const Reply *key = r.multi_bulk[i],
|
|
||||||
*value = r.multi_bulk[i + 1];
|
|
||||||
|
|
||||||
data[key->bulk] << value->bulk;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = st->Unserialize(obj, data);
|
|
||||||
if (obj)
|
|
||||||
{
|
|
||||||
obj->object_id = this->id;
|
|
||||||
obj->UpdateCache(data);
|
|
||||||
|
|
||||||
/* Insert new object values */
|
|
||||||
for (const auto &[key, value] : data.data)
|
|
||||||
{
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("SADD");
|
|
||||||
args.push_back("value:" + st->GetName() + ":" + key + ":" + value->str());
|
|
||||||
args.push_back(Anope::ToString(obj->object_id));
|
|
||||||
|
|
||||||
/* Add to value -> object id set */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Anope::string> args;
|
|
||||||
args.emplace_back("SADD");
|
|
||||||
args.push_back("ids:" + st->GetName());
|
|
||||||
args.push_back(Anope::ToString(obj->object_id));
|
|
||||||
|
|
||||||
/* Add to type -> id set */
|
|
||||||
me->redis->SendCommand(NULL, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Transaction end */
|
|
||||||
me->redis->CommitTransaction();
|
|
||||||
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
||||||
MODULE_INIT(DatabaseRedis)
|
|
||||||
@@ -120,6 +120,12 @@ public:
|
|||||||
{
|
{
|
||||||
if (this->sql)
|
if (this->sql)
|
||||||
{
|
{
|
||||||
|
if (!obj->ShouldCommit())
|
||||||
|
{
|
||||||
|
OnSerializableDestruct(obj);
|
||||||
|
continue; // Non-committable object.
|
||||||
|
}
|
||||||
|
|
||||||
Serialize::Type *s_type = obj->GetSerializableType();
|
Serialize::Type *s_type = obj->GetSerializableType();
|
||||||
if (!s_type)
|
if (!s_type)
|
||||||
continue;
|
continue;
|
||||||
@@ -168,7 +174,7 @@ public:
|
|||||||
{
|
{
|
||||||
const auto &block = conf.GetModule(this);
|
const auto &block = conf.GetModule(this);
|
||||||
this->sql.SetServiceName(block.Get<const Anope::string>("engine"));
|
this->sql.SetServiceName(block.Get<const Anope::string>("engine"));
|
||||||
this->prefix = block.Get<const Anope::string>("prefix", "anope_db_");
|
this->prefix = block.Get<const Anope::string>("prefix", "anope21_");
|
||||||
this->import = block.Get<bool>("import");
|
this->import = block.Get<bool>("import");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +262,7 @@ public:
|
|||||||
Data data;
|
Data data;
|
||||||
|
|
||||||
for (const auto &[key, value] : res.Row(j))
|
for (const auto &[key, value] : res.Row(j))
|
||||||
data[key] << value;
|
data.StoreInternal(key, value);
|
||||||
|
|
||||||
Serializable *obj = sb->Unserialize(NULL, data);
|
Serializable *obj = sb->Unserialize(NULL, data);
|
||||||
if (obj)
|
if (obj)
|
||||||
|
|||||||
@@ -104,6 +104,12 @@ public:
|
|||||||
{
|
{
|
||||||
if (obj && this->SQL)
|
if (obj && this->SQL)
|
||||||
{
|
{
|
||||||
|
if (!obj->ShouldCommit())
|
||||||
|
{
|
||||||
|
OnSerializableDestruct(obj);
|
||||||
|
continue; // Non-committable object.
|
||||||
|
}
|
||||||
|
|
||||||
Serialize::Type *s_type = obj->GetSerializableType();
|
Serialize::Type *s_type = obj->GetSerializableType();
|
||||||
if (!s_type)
|
if (!s_type)
|
||||||
continue;
|
continue;
|
||||||
@@ -152,7 +158,7 @@ public:
|
|||||||
const auto &block = conf.GetModule(this);
|
const auto &block = conf.GetModule(this);
|
||||||
|
|
||||||
this->SQL.SetServiceName(block.Get<const Anope::string>("engine"));
|
this->SQL.SetServiceName(block.Get<const Anope::string>("engine"));
|
||||||
this->prefix = block.Get<const Anope::string>("prefix", "anope_db_");
|
this->prefix = block.Get<const Anope::string>("prefix", "anope21_");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnSerializableConstruct(Serializable *obj) override
|
void OnSerializableConstruct(Serializable *obj) override
|
||||||
@@ -220,7 +226,7 @@ public:
|
|||||||
Data data;
|
Data data;
|
||||||
|
|
||||||
for (const auto &[key, value] : row)
|
for (const auto &[key, value] : row)
|
||||||
data[key] << value;
|
data.StoreInternal(key, value);
|
||||||
|
|
||||||
Serializable *s = NULL;
|
Serializable *s = NULL;
|
||||||
auto it = obj->objects.find(id);
|
auto it = obj->objects.find(id);
|
||||||
|
|||||||
+13
-12
@@ -534,8 +534,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Times out after a few seconds */
|
/* Times out after a few seconds */
|
||||||
void Tick() override
|
bool Tick() override
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reply(Packet *p) override
|
void Reply(Packet *p) override
|
||||||
@@ -718,7 +719,7 @@ public:
|
|||||||
|
|
||||||
MyManager(Module *creator)
|
MyManager(Module *creator)
|
||||||
: Manager(creator)
|
: Manager(creator)
|
||||||
, Timer(300, true)
|
, Timer(300)
|
||||||
, serial(Anope::CurTime)
|
, serial(Anope::CurTime)
|
||||||
, cur_id(Anope::RandomNumber())
|
, cur_id(Anope::RandomNumber())
|
||||||
{
|
{
|
||||||
@@ -729,7 +730,7 @@ public:
|
|||||||
delete udpsock;
|
delete udpsock;
|
||||||
delete tcpsock;
|
delete tcpsock;
|
||||||
|
|
||||||
for (std::map<unsigned short, Request *>::iterator it = this->requests.begin(), it_end = this->requests.end(); it != it_end;)
|
for (auto it = this->requests.begin(), it_end = this->requests.end(); it != it_end;)
|
||||||
{
|
{
|
||||||
Request *request = it->second;
|
Request *request = it->second;
|
||||||
++it;
|
++it;
|
||||||
@@ -909,7 +910,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<unsigned short, Request *>::iterator it = this->requests.find(recv_packet.id);
|
auto it = this->requests.find(recv_packet.id);
|
||||||
if (it == this->requests.end())
|
if (it == this->requests.end())
|
||||||
{
|
{
|
||||||
Log(LOG_DEBUG_2) << "Resolver: Received an answer for something we didn't request";
|
Log(LOG_DEBUG_2) << "Resolver: Received an answer for something we didn't request";
|
||||||
@@ -1011,7 +1012,7 @@ public:
|
|||||||
return serial;
|
return serial;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tick() override
|
bool Tick() override
|
||||||
{
|
{
|
||||||
Log(LOG_DEBUG_2) << "Resolver: Purging DNS cache";
|
Log(LOG_DEBUG_2) << "Resolver: Purging DNS cache";
|
||||||
|
|
||||||
@@ -1025,6 +1026,7 @@ public:
|
|||||||
if (req.created + static_cast<time_t>(req.ttl) < Anope::CurTime)
|
if (req.created + static_cast<time_t>(req.ttl) < Anope::CurTime)
|
||||||
this->cache.erase(it);
|
this->cache.erase(it);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1043,7 +1045,7 @@ private:
|
|||||||
*/
|
*/
|
||||||
bool CheckCache(Request *request)
|
bool CheckCache(Request *request)
|
||||||
{
|
{
|
||||||
cache_map::iterator it = this->cache.find(*request);
|
auto it = this->cache.find(*request);
|
||||||
if (it != this->cache.end())
|
if (it != this->cache.end())
|
||||||
{
|
{
|
||||||
Query &record = it->second;
|
Query &record = it->second;
|
||||||
@@ -1070,7 +1072,7 @@ public:
|
|||||||
|
|
||||||
~ModuleDNS() override
|
~ModuleDNS() override
|
||||||
{
|
{
|
||||||
for (std::map<int, Socket *>::const_iterator it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;)
|
for (auto it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;)
|
||||||
{
|
{
|
||||||
Socket *s = it->second;
|
Socket *s = it->second;
|
||||||
++it;
|
++it;
|
||||||
@@ -1093,11 +1095,10 @@ public:
|
|||||||
refresh = block.Get<int>("refresh", "3600");
|
refresh = block.Get<int>("refresh", "3600");
|
||||||
|
|
||||||
std::vector<std::pair<Anope::string, short> > notify;
|
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");
|
||||||
Anope::string nip = n.Get<Anope::string>("ip");
|
auto nport = n.Get<short>("port");
|
||||||
short nport = n.Get<short>("port");
|
|
||||||
|
|
||||||
notify.emplace_back(nip, nport);
|
notify.emplace_back(nip, nport);
|
||||||
}
|
}
|
||||||
@@ -1149,7 +1150,7 @@ public:
|
|||||||
|
|
||||||
void OnModuleUnload(User *u, Module *m) override
|
void OnModuleUnload(User *u, Module *m) override
|
||||||
{
|
{
|
||||||
for (std::map<unsigned short, Request *>::iterator it = this->manager.requests.begin(), it_end = this->manager.requests.end(); it != it_end;)
|
for (auto it = this->manager.requests.begin(), it_end = this->manager.requests.end(); it != it_end;)
|
||||||
{
|
{
|
||||||
unsigned short id = it->first;
|
unsigned short id = it->first;
|
||||||
Request *req = it->second;
|
Request *req = it->second;
|
||||||
|
|||||||
+3
-6
@@ -126,9 +126,8 @@ public:
|
|||||||
this->add_to_akill = block.Get<bool>("add_to_akill", "yes");
|
this->add_to_akill = block.Get<bool>("add_to_akill", "yes");
|
||||||
|
|
||||||
this->blacklists.clear();
|
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 blacklist;
|
||||||
|
|
||||||
blacklist.name = bl.Get<Anope::string>("name");
|
blacklist.name = bl.Get<Anope::string>("name");
|
||||||
@@ -137,9 +136,8 @@ public:
|
|||||||
blacklist.bantime = bl.Get<time_t>("time", "4h");
|
blacklist.bantime = bl.Get<time_t>("time", "4h");
|
||||||
blacklist.reason = bl.Get<Anope::string>("reason");
|
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;
|
Blacklist::Reply r;
|
||||||
|
|
||||||
r.code = reply.Get<int>("code");
|
r.code = reply.Get<int>("code");
|
||||||
@@ -153,9 +151,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->exempts.clear();
|
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"));
|
this->exempts.insert(bl.Get<Anope::string>("ip"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
/// BEGIN CMAKE
|
||||||
|
/// target_link_libraries(${SO} PRIVATE "vendored_bcrypt")
|
||||||
|
/// END CMAKE
|
||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
#include "bcrypt/crypt_blowfish.c"
|
extern "C"
|
||||||
|
{
|
||||||
|
#include "bcrypt/crypt_blowfish.h"
|
||||||
|
}
|
||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "modules/encryption.h"
|
#include "modules/encryption.h"
|
||||||
|
|||||||
@@ -12,10 +12,17 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
/// BEGIN CMAKE
|
||||||
|
/// target_link_libraries(${SO} PRIVATE "vendored_md5")
|
||||||
|
/// END CMAKE
|
||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "modules/encryption.h"
|
#include "modules/encryption.h"
|
||||||
|
|
||||||
#include "md5/md5.c"
|
extern "C"
|
||||||
|
{
|
||||||
|
#include "md5/md5.h"
|
||||||
|
}
|
||||||
|
|
||||||
class MD5Context final
|
class MD5Context final
|
||||||
: public Encryption::Context
|
: public Encryption::Context
|
||||||
|
|||||||
@@ -12,10 +12,14 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
/// BEGIN CMAKE
|
||||||
|
/// target_link_libraries(${SO} PRIVATE "vendored_sha1")
|
||||||
|
/// END CMAKE
|
||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "modules/encryption.h"
|
#include "modules/encryption.h"
|
||||||
|
|
||||||
#include "sha1/sha1.c"
|
#include "sha1/sha1.h"
|
||||||
|
|
||||||
class SHA1Context final
|
class SHA1Context final
|
||||||
: public Encryption::Context
|
: public Encryption::Context
|
||||||
|
|||||||
@@ -12,10 +12,14 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
/// BEGIN CMAKE
|
||||||
|
/// target_link_libraries(${SO} PRIVATE "vendored_sha2")
|
||||||
|
/// END CMAKE
|
||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
#include "sha2/sha2.c"
|
#include "sha2/sha2.h"
|
||||||
|
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "modules/encryption.h"
|
#include "modules/encryption.h"
|
||||||
|
|||||||
+76
-4
@@ -405,6 +405,80 @@ public:
|
|||||||
QueueRequest(mod);
|
QueueRequest(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Anope::string EscapeDN(const Anope::string &str) const override
|
||||||
|
{
|
||||||
|
if (str.empty())
|
||||||
|
return str;
|
||||||
|
|
||||||
|
Anope::string newstr;
|
||||||
|
newstr.str().reserve(str.length());
|
||||||
|
for (size_t idx = 0; idx < str.length(); ++idx)
|
||||||
|
{
|
||||||
|
const char chr = str[idx];
|
||||||
|
if (chr == '\0')
|
||||||
|
{
|
||||||
|
newstr.append("\\00");
|
||||||
|
}
|
||||||
|
else if (chr == '"' || chr == '+' || chr == ',' || chr == ';' ||
|
||||||
|
chr == '<' || chr == '=' || chr == '>' || chr == '\\')
|
||||||
|
{
|
||||||
|
newstr.push_back('\\');
|
||||||
|
newstr.push_back(chr);
|
||||||
|
}
|
||||||
|
else if (idx == 0 && (chr == '#' || chr == ' '))
|
||||||
|
{
|
||||||
|
newstr.push_back('\\');
|
||||||
|
newstr.push_back(chr);
|
||||||
|
}
|
||||||
|
else if (idx == str.length() - 1 && chr == ' ')
|
||||||
|
{
|
||||||
|
newstr.push_back('\\');
|
||||||
|
newstr.push_back(chr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newstr.push_back(chr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Anope::string EscapeSF(const Anope::string &str) const override
|
||||||
|
{
|
||||||
|
if (str.empty())
|
||||||
|
return str;
|
||||||
|
|
||||||
|
Anope::string newstr;
|
||||||
|
newstr.str().reserve(str.length());
|
||||||
|
for (size_t idx = 0; idx < str.length(); ++idx)
|
||||||
|
{
|
||||||
|
const char chr = str[idx];
|
||||||
|
switch (chr)
|
||||||
|
{
|
||||||
|
case '\0':
|
||||||
|
newstr.append("\\00");
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
newstr.append("\\28");
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
newstr.append("\\29");
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
newstr.append("\\2A");
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
newstr.append("\\5C");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
newstr.push_back(chr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newstr;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void BuildReply(int res, LDAPRequest *req)
|
void BuildReply(int res, LDAPRequest *req)
|
||||||
{
|
{
|
||||||
@@ -560,7 +634,7 @@ public:
|
|||||||
{
|
{
|
||||||
const Anope::string &cname = it->first;
|
const Anope::string &cname = it->first;
|
||||||
LDAPService *s = it->second;
|
LDAPService *s = it->second;
|
||||||
int i;
|
size_t i;
|
||||||
|
|
||||||
++it;
|
++it;
|
||||||
|
|
||||||
@@ -580,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");
|
const Anope::string &connname = ldap.Get<const Anope::string>("name", "ldap/main");
|
||||||
|
|
||||||
if (this->LDAPServices.find(connname) == this->LDAPServices.end())
|
if (this->LDAPServices.find(connname) == this->LDAPServices.end())
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ public:
|
|||||||
{
|
{
|
||||||
const Anope::string &cname = it->first;
|
const Anope::string &cname = it->first;
|
||||||
MySQLService *s = it->second;
|
MySQLService *s = it->second;
|
||||||
int i;
|
size_t i;
|
||||||
|
|
||||||
++it;
|
++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");
|
const Anope::string &connname = block.Get<const Anope::string>("name", "mysql/main");
|
||||||
|
|
||||||
if (this->MySQLServices.find(connname) == this->MySQLServices.end())
|
if (this->MySQLServices.find(connname) == this->MySQLServices.end())
|
||||||
@@ -575,7 +574,7 @@ Query MySQLService::BuildInsert(const Anope::string &table, Serializable::Id id,
|
|||||||
for (const auto &known_col : this->active_schema[table])
|
for (const auto &known_col : this->active_schema[table])
|
||||||
{
|
{
|
||||||
if (data.data.count(known_col) == 0)
|
if (data.data.count(known_col) == 0)
|
||||||
data[known_col] << "";
|
data.data[known_col];
|
||||||
}
|
}
|
||||||
|
|
||||||
Anope::string query_text = "INSERT INTO `" + table + "` (`id`";
|
Anope::string query_text = "INSERT INTO `" + table + "` (`id`";
|
||||||
@@ -593,9 +592,7 @@ Query MySQLService::BuildInsert(const Anope::string &table, Serializable::Id id,
|
|||||||
Query query(query_text);
|
Query query(query_text);
|
||||||
for (auto &[field, value] : data.data)
|
for (auto &[field, value] : data.data)
|
||||||
{
|
{
|
||||||
Anope::string buf;
|
auto buf = value;
|
||||||
*value >> buf;
|
|
||||||
|
|
||||||
auto escape = true;
|
auto escape = true;
|
||||||
switch (data.GetType(field))
|
switch (data.GetType(field))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public:
|
|||||||
int err = regcomp(&this->regbuf, expr.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE);
|
int err = regcomp(&this->regbuf, expr.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
char buf[BUFSIZE];
|
char buf[256];
|
||||||
regerror(err, &this->regbuf, buf, sizeof(buf));
|
regerror(err, &this->regbuf, buf, sizeof(buf));
|
||||||
regfree(&this->regbuf);
|
regfree(&this->regbuf);
|
||||||
throw RegexException("Error in regex " + expr + ": " + buf);
|
throw RegexException("Error in regex " + expr + ": " + buf);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public:
|
|||||||
int err = regcomp(&this->regbuf, expr.c_str(), REG_EXTENDED | REG_NOSUB);
|
int err = regcomp(&this->regbuf, expr.c_str(), REG_EXTENDED | REG_NOSUB);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
char buf[BUFSIZE];
|
char buf[256];
|
||||||
regerror(err, &this->regbuf, buf, sizeof(buf));
|
regerror(err, &this->regbuf, buf, sizeof(buf));
|
||||||
regfree(&this->regbuf);
|
regfree(&this->regbuf);
|
||||||
throw RegexException("Error in regex " + expr + ": " + buf);
|
throw RegexException("Error in regex " + expr + ": " + buf);
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public:
|
|||||||
{
|
{
|
||||||
const Anope::string &cname = it->first;
|
const Anope::string &cname = it->first;
|
||||||
SQLiteService *s = it->second;
|
SQLiteService *s = it->second;
|
||||||
int i, num;
|
size_t i, num;
|
||||||
++it;
|
++it;
|
||||||
|
|
||||||
for (i = 0, num = config.CountBlock("sqlite"); i < num; ++i)
|
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");
|
Anope::string connname = block.Get<const Anope::string>("name", "sqlite/main");
|
||||||
|
|
||||||
if (this->SQLiteServices.find(connname) == this->SQLiteServices.end())
|
if (this->SQLiteServices.find(connname) == this->SQLiteServices.end())
|
||||||
@@ -311,7 +310,7 @@ Query SQLiteService::BuildInsert(const Anope::string &table, Serializable::Id id
|
|||||||
for (const auto &known_col : this->active_schema[table])
|
for (const auto &known_col : this->active_schema[table])
|
||||||
{
|
{
|
||||||
if (known_col != "id" && known_col != "timestamp" && data.data.count(known_col) == 0)
|
if (known_col != "id" && known_col != "timestamp" && data.data.count(known_col) == 0)
|
||||||
data[known_col] << "";
|
data.data[known_col];
|
||||||
}
|
}
|
||||||
|
|
||||||
Anope::string query_text = "REPLACE INTO `" + table + "` (";
|
Anope::string query_text = "REPLACE INTO `" + table + "` (";
|
||||||
@@ -331,9 +330,7 @@ Query SQLiteService::BuildInsert(const Anope::string &table, Serializable::Id id
|
|||||||
Query query(query_text);
|
Query query(query_text);
|
||||||
for (auto &[field, value] : data.data)
|
for (auto &[field, value] : data.data)
|
||||||
{
|
{
|
||||||
Anope::string buf;
|
auto buf = value;
|
||||||
*value >> buf;
|
|
||||||
|
|
||||||
auto escape = true;
|
auto escape = true;
|
||||||
switch (data.GetType(field))
|
switch (data.GetType(field))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -331,10 +331,8 @@ public:
|
|||||||
throw ConfigException("Unable to find http reference, is httpd loaded?");
|
throw ConfigException("Unable to find http reference, is httpd loaded?");
|
||||||
|
|
||||||
xmlrpcinterface.tokens.clear();
|
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;
|
RPC::Token token;
|
||||||
token.token = block.Get<const Anope::string>("token");
|
token.token = block.Get<const Anope::string>("token");
|
||||||
if (!token.token.empty())
|
if (!token.token.empty())
|
||||||
|
|||||||
+1
-1
@@ -147,7 +147,7 @@ public:
|
|||||||
if (params.empty())
|
if (params.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CommandInfo::map::const_iterator it = Config->Fantasy.end();
|
auto it = Config->Fantasy.end();
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
for (unsigned max = params.size(); it == Config->Fantasy.end() && max > 0; --max)
|
for (unsigned max = params.size(); it == Config->Fantasy.end() && max > 0; --max)
|
||||||
{
|
{
|
||||||
|
|||||||
+27
-10
@@ -88,7 +88,7 @@ public:
|
|||||||
CommandNSSetGreet(Module *creator, const Anope::string &sname = "nickserv/set/greet", size_t min = 0) : Command(creator, sname, min, min + 1)
|
CommandNSSetGreet(Module *creator, const Anope::string &sname = "nickserv/set/greet", size_t min = 0) : Command(creator, sname, min, min + 1)
|
||||||
{
|
{
|
||||||
this->SetDesc(_("Associate a greet message with your nickname"));
|
this->SetDesc(_("Associate a greet message with your nickname"));
|
||||||
this->SetSyntax(_("\037message\037"));
|
this->SetSyntax(_("[\037message\037]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m)
|
||||||
@@ -136,11 +136,19 @@ public:
|
|||||||
this->SendSyntax(source);
|
this->SendSyntax(source);
|
||||||
source.Reply(" ");
|
source.Reply(" ");
|
||||||
source.Reply(_(
|
source.Reply(_(
|
||||||
"Makes the given message the greet of your nickname, that "
|
"Changes your greet message. This message will be displayed when joining a channel "
|
||||||
"will be displayed when joining a channel that has GREET "
|
"that has GREET option enabled, provided that you have the necessary access on it."
|
||||||
"option enabled, provided that you have the necessary "
|
|
||||||
"access on it."
|
|
||||||
));
|
));
|
||||||
|
|
||||||
|
ExampleWrapper()
|
||||||
|
.AddEntry(_("I come in peace"), _(
|
||||||
|
"Changes your greet message to \035I come in peace\035."
|
||||||
|
))
|
||||||
|
.AddEntry("", _(
|
||||||
|
"Removes your greet message."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -152,7 +160,7 @@ public:
|
|||||||
CommandNSSASetGreet(Module *creator) : CommandNSSetGreet(creator, "nickserv/saset/greet", 1)
|
CommandNSSASetGreet(Module *creator) : CommandNSSetGreet(creator, "nickserv/saset/greet", 1)
|
||||||
{
|
{
|
||||||
this->ClearSyntax();
|
this->ClearSyntax();
|
||||||
this->SetSyntax(_("\037nickname\037 \037message\037"));
|
this->SetSyntax(_("\037nickname\037 [\037message\037]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override
|
||||||
@@ -165,11 +173,20 @@ public:
|
|||||||
this->SendSyntax(source);
|
this->SendSyntax(source);
|
||||||
source.Reply(" ");
|
source.Reply(" ");
|
||||||
source.Reply(_(
|
source.Reply(_(
|
||||||
"Makes the given message the greet of the nickname, that "
|
"Changes the greet message of the specified nickname. This message will be displayed "
|
||||||
"will be displayed when joining a channel that has GREET "
|
"when joining a channel that has GREET option enabled, provided that the user has the "
|
||||||
"option enabled, provided that the user has the necessary "
|
"necessary access on it."
|
||||||
"access on it."
|
|
||||||
));
|
));
|
||||||
|
|
||||||
|
ExampleWrapper()
|
||||||
|
.AddEntry(_("alien I come in peace"), _(
|
||||||
|
"Changes the greet message of \035alien\035 to \035I come in peace\035."
|
||||||
|
))
|
||||||
|
.AddEntry("zebra", _(
|
||||||
|
"Removes the greet message of \035zebra\035."
|
||||||
|
))
|
||||||
|
.SendTo(source);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user