diff --git a/.appveyor.yml b/.appveyor.yml index 193c25a3..6df0cd1d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -4,48 +4,27 @@ # TODO: test msvc version: 0.0.0.0.1-branch-{branch}-build-{build} clone_depth: 10 -environment: - matrix: - - cygwin_url: https://cygwin.com/setup-x86_64.exe - build_with: cmake - - cygwin_url: https://cygwin.com/setup-x86.exe - build_with: cmake - - cygwin_url: https://cygwin.com/setup-x86_64.exe - build_with: autoconf - - cygwin_url: https://cygwin.com/setup-x86.exe - build_with: autoconf install: - - ps: Invoke-WebRequest $env:cygwin_url -OutFile c:\cygwin-setup.exe + - ps: Invoke-WebRequest https://cygwin.com/setup-x86_64.exe -OutFile c:\cygwin-setup.exe # libcrypt-devel is needed only on x86_64 and only for modperl... probably some dependency problem. - - c:\cygwin-setup.exe --quiet-mode --no-shortcuts --no-startmenu --no-desktop --upgrade-also --only-site --site http://cygwin.mirror.constant.com/ --root c:\cygwin-root --local-package-dir c:\cygwin-setup-cache --packages automake,gcc-g++,make,pkg-config,wget,openssl-devel,libicu-devel,zlib-devel,libcrypt-devel,perl,python3-devel,swig,libsasl2-devel,libQt5Core-devel,cmake,libboost-devel,gettext-devel + - c:\cygwin-setup.exe --quiet-mode --no-shortcuts --no-startmenu --no-desktop --upgrade-also --only-site --site http://cygwin.mirror.constant.com/ --root c:\cygwin-root --local-package-dir c:\cygwin-setup-cache --packages gcc-g++,make,pkg-config,wget,libssl-devel,libicu-devel,zlib-devel,libcrypt-devel,perl,python3-devel,swig,libsasl2-devel,libQt5Core-devel,cmake,libboost-devel,gettext-devel,libargon2-devel - c:\cygwin-root\bin\sh -lc "echo Hi" - c:\cygwin-root\bin\sh -lc "uname -a" - c:\cygwin-root\bin\sh -lc "cat /proc/cpuinfo" - c:\cygwin-root\bin\sh -lc "cat /proc/meminfo" - c:\cygwin-root\bin\sh -lc "cygcheck -s -v > $APPVEYOR_BUILD_FOLDER/cygcheck.log 2>&1" - ps: Push-AppveyorArtifact cygcheck.log - - ps: | - if ($env:build_with -eq "cmake") { - $env:cfg_suffix = ".sh" - $env:unittest = "unittest" - $env:inttest = "inttest" - } else { - $env:cfg_suffix = "" - $env:unittest = "test" - $env:inttest = "test2" - } # stdin is broken at AppVeyor, so we open it explicitly as /dev/null build_script: - git submodule update --init - - c:\cygwin-root\bin\sh -lc "cd $APPVEYOR_BUILD_FOLDER; ./autogen.sh < /dev/null" - mkdir build - - c:\cygwin-root\bin\sh -lc "cd $APPVEYOR_BUILD_FOLDER/build; ../configure$cfg_suffix --enable-charset --enable-zlib --enable-openssl --enable-perl --enable-python --enable-cyrus < /dev/null; result=$?; if [[ $build_with == cmake ]]; then cmake --system-information > config.log; fi; appveyor PushArtifact config.log; exit $result" + - c:\cygwin-root\bin\sh -lc "cd $APPVEYOR_BUILD_FOLDER/build; env ZNC_QT_VER=5 ../configure --enable-charset --enable-zlib --enable-openssl --enable-perl --enable-python --enable-cyrus --enable-argon < /dev/null; result=$?; cmake --system-information > config.log; appveyor PushArtifact config.log; exit $result" - c:\cygwin-root\bin\sh -lc "cd $APPVEYOR_BUILD_FOLDER/build; make VERBOSE=1 -j2 < /dev/null" - c:\cygwin-root\bin\sh -lc "cd $APPVEYOR_BUILD_FOLDER/build; make install < /dev/null" - c:\cygwin-root\bin\sh -lc "znc --version" # fix fork() - c:\cygwin-root\bin\sh -lc "find /usr/local/lib/znc -iname '*.dll' -o -iname '*.so' | tee /tmp/files-to-rebase" - - c:\cygwin-root\bin\sh -lc "rebaseall -v -T /tmp/files-to-rebase" + - c:\cygwin-root\bin\sh -lc "rebase -s -v $(cat /tmp/files-to-rebase)" test_script: - - c:\cygwin-root\bin\sh -lc "cd $APPVEYOR_BUILD_FOLDER/build; make VERBOSE=1 $unittest < /dev/null" - - c:\cygwin-root\bin\sh -lc "cd $APPVEYOR_BUILD_FOLDER/build; make VERBOSE=1 $inttest < /dev/null" + - c:\cygwin-root\bin\sh -lc "cd $APPVEYOR_BUILD_FOLDER/build; make VERBOSE=1 unittest < /dev/null" + - c:\cygwin-root\bin\sh -lc "cd $APPVEYOR_BUILD_FOLDER/build; env ZNC_QT_VER=5 make VERBOSE=1 inttest < /dev/null" diff --git a/.ci/Jenkinsfile.crowdin b/.ci/Jenkinsfile.crowdin new file mode 100644 index 00000000..bbdd07fe --- /dev/null +++ b/.ci/Jenkinsfile.crowdin @@ -0,0 +1,121 @@ +#!groovy +// This script is run daily by https://jenkins.znc.in/ to: +// * upload new English strings to https://crowdin.com/project/znc-bouncer +// * download new translations +// * commits results to ZNC repo + +import groovy.json.JsonSlurper; +import groovy.json.JsonOutput; + +def upstream_user = 'znc' +def upstream_repo = 'znc' +def my_user = 'znc-jenkins' +def my_repo = 'znc' +def branches = ['master', '1.9.x'] + +def pr_mode = false + +timestamps { + node { + timeout(time: 30, unit: 'MINUTES') { + stage('WsCleanup') { + step([$class: 'WsCleanup']) + } + def tasks = [:] + def crowdin_cli = "java -jar ${tool 'crowdin-cli'}/crowdin-cli.jar" + def crowdin_cli_suffix = "--config .ci/crowdin.yml" + for (def branch in branches) { + def upstream_branch = branch + def my_branch = "l10n_${branch}" + tasks[branch] = { + stage(upstream_branch) { + dir(upstream_branch) { + stage("Checkout ${upstream_branch}") { + checkout([$class: 'GitSCM', branches: [[name: "*/${upstream_branch}"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'SubmoduleOption', recursiveSubmodules: true]], userRemoteConfigs: [[credentialsId: '6ef10f80-20dc-4661-af45-52a6e1e15749', name: 'upstream', url: "git@github.com:${upstream_user}/${upstream_repo}.git"]]]) + } + stage("Prepare strings for ${upstream_branch}") { + dir("build") { + sh "cmake .." + sh 'make translation' + } + sh 'rm -rf build/' + sh 'git status' + } + stage("Crowdin for ${upstream_branch}") { + withCredentials([string(credentialsId: '11c7e2b4-f990-4670-98a4-c89d2a5a2f43', variable: 'CROWDIN_API_KEY')]) { + withEnv(['CROWDIN_BASE_PATH='+pwd()]) { + sh "$crowdin_cli upload sources --branch ${upstream_branch} ${crowdin_cli_suffix}" + // sh "$crowdin_cli upload translations --branch ${upstream_branch} ${crowdin_cli_suffix}" + sh "$crowdin_cli download --branch ${upstream_branch} ${crowdin_cli_suffix}" + } + def headers = [[maskValue: true, name: 'Authorization', value: "Bearer ${env.CROWDIN_API_KEY}"], [maskValue: false, name: 'User-Agent', value: 'https://github.com/znc/znc/blob/master/.ci/Jenkinsfile.crowdin']] + def contributors = httpRequest consoleLogResponseBody: true, customHeaders: headers, url: "https://crowdin.com/api/v2/projects/289533/members?limit=500" + writeFile file: 'contributors.tmp', text: contributors.content + } + sh 'LANG=C.UTF-8 find . -name "*.po" -exec msgfilter -i "{}" -o "{}.replacement" .ci/cleanup-po.pl ";"' + sh 'find . -name "*.po" -exec mv "{}.replacement" "{}" ";"' + sh '.ci/crowdin-contributors.py < contributors.tmp' + sh 'rm contributors.tmp' + } + stage("Push ${upstream_branch}") { + sh 'git config user.name "ZNC-Jenkins"' + sh 'git config user.email jenkins@znc.in' + sh 'git status' + def git_status_short = sh (script: 'git status --short', returnStdout: true) + def modified_locales = sh ( + script: 'git status --short | grep -o -E "[^ ]+\\.po$" | sed "s/.po//g" | grep -o -E "[a-z]{2}_[A-Z]{2}$" | sort -u | tr "\\n" " " | sed -E "s/ $//"', + returnStdout: true + ) + sh 'git add .' + try { + sh "git commit -m 'Update translations from Crowdin for ${modified_locales}'" + } catch(e) { + echo 'No changes found' + return + } + if (!pr_mode) { + sshagent(credentials: ['baf2df74-935d-40e5-b20f-076e92fa3e9f']) { + sh "git push upstream HEAD:refs/heads/${upstream_branch}" + } + return + } + // Create pull request if it doesn't exist yet + // Note: the following code hasn't been executed for long time, so probably nothing here works anymore, APIs might have changed, etc. + sh "git remote add my git@github.com:${my_user}/${my_repo}.git" + sshagent(credentials: ['6ef10f80-20dc-4661-af45-52a6e1e15749']) { + sh "git push my HEAD:refs/heads/${my_branch} -f" + } + withCredentials([string(credentialsId: '7a2546ae-8a29-4eab-921c-6a4803456dce', variable: 'GITHUB_OAUTH_KEY')]) { + def headers = [[maskValue: true, name: 'Authorization', value: "token ${env.GITHUB_OAUTH_KEY}"], [maskValue: false, name: 'Accept', value: 'application/vnd.github.v3+json'], [maskValue: false, name: 'User-Agent', value: 'https://github.com/znc/znc/blob/master/.ci/Jenkinsfile.crowdin']] + def pulls = httpRequest consoleLogResponseBody: true, customHeaders: headers, url: "https://api.github.com/repos/${upstream_user}/${upstream_repo}/pulls?head=${my_user}:${my_branch}&base=${upstream_branch}" + pulls = new JsonSlurper().parseText(pulls.content) + if (!pulls) { + def bodyContents = "Crowdin: https://crowdin.com/project/znc-bouncer\nJenkins Build: ${env.BUILD_URL}" + bodyContents += "\n\nModified locales:\n${modified_locales}" + bodyContents += "\n\n
Modified files${git_status_short}
" + + httpRequest consoleLogResponseBody: true, + customHeaders: headers, + url: "https://api.github.com/repos/${upstream_user}/${upstream_repo}/pulls", + httpMode: 'POST', + requestBody: JsonOutput.toJson( + [ + head: my_user + ':' + my_branch, + base: upstream_branch, + title: "Update translations in ${upstream_branch} (${modified_locales})", + body: bodyContents + ] + ) + } + } + } + } + } + } + } + parallel tasks + } + } +} + +// vim: set ts=2 sw=2 et: diff --git a/.ci/cleanup-po.pl b/.ci/cleanup-po.pl new file mode 100755 index 00000000..392e890b --- /dev/null +++ b/.ci/cleanup-po.pl @@ -0,0 +1,18 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use v5.10; + +local $/; +my $text = <>; + +if ($ENV{MSGFILTER_MSGID}) { + print $text; +} else { + for (split(/^/, $text)) { + next if /^PO-Revision-Date:/; + s/^Last-Translator: \K.*/Various people/; + print; + } +} diff --git a/.ci/crowdin-contributors.py b/.ci/crowdin-contributors.py new file mode 100755 index 00000000..b1bdebc8 --- /dev/null +++ b/.ci/crowdin-contributors.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +import json +import sys + +array = [] + +data = json.load(sys.stdin) +for user in data['data']: + user = user['data'] + if user['fullName']: + array.append('* {} ({})'.format(user['username'], user['fullName'])) + else: + array.append('* ' + user['username']) + +array.sort(key=lambda x: x.lower()) + +sys.stdout = open('TRANSLATORS.md', 'wt', encoding='utf-8') + +print('These people helped translating ZNC to various languages:') +print() +for u in array: + print(u) +print() +print('Generated from https://crowdin.com/project/znc-bouncer') diff --git a/.ci/crowdin.yml b/.ci/crowdin.yml new file mode 100644 index 00000000..ec27c0d5 --- /dev/null +++ b/.ci/crowdin.yml @@ -0,0 +1,13 @@ +# https://crowdin.com/project/znc-bouncer +project_id: 289533 +preserve_hierarchy: true +api_token_env: CROWDIN_API_KEY +base_path_env: CROWDIN_BASE_PATH + +files: + - source: /src/po/znc.pot + translation: /src/po/znc.%locale_with_underscore%.po + update_option: update_as_unapproved + - source: "/modules/po/*.pot" + translation: /modules/po/%file_name%.%locale_with_underscore%.po + update_option: update_as_unapproved diff --git a/.travis-generate-docs.sh b/.ci/generate-docs.sh similarity index 86% rename from .travis-generate-docs.sh rename to .ci/generate-docs.sh index c60ac52d..c2e399f6 100755 --- a/.travis-generate-docs.sh +++ b/.ci/generate-docs.sh @@ -7,7 +7,7 @@ doxygen cd "$HOME" git clone --depth=1 --branch=gh-pages github:znc/docs.git gh-pages || exit 1 -cd "$TRAVIS_BUILD_DIR/doc/html/" +cd "$GITHUB_WORKSPACE/doc/html/" mv ~/gh-pages/.git ./ echo docs.znc.in > CNAME git add -A @@ -28,9 +28,9 @@ if [[ ! -f ~/docs_need_commit ]]; then fi git commit -F- < unittest-cmake-coverage.txt + llvm-cov show -show-line-counts-or-regions -instr-profile=inttest.profdata /usr/local/bin/znc > inttest-znc-coverage.txt + find /usr/local/lib/znc -name '*.so' -or -name '*.bundle' | while read f; do llvm-cov show -show-line-counts-or-regions -instr-profile=inttest.profdata $f > inttest-$(basename $f)-coverage.txt; done + ;; +esac diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..64284b90 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +--- +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/ubuntu_deps.sh b/.github/ubuntu_deps.sh new file mode 100644 index 00000000..bc0bec58 --- /dev/null +++ b/.github/ubuntu_deps.sh @@ -0,0 +1,4 @@ +sudo apt-get update +sudo apt-get install -y tcl-dev libsasl2-dev libicu-dev swig qt6-base-dev libboost-locale-dev libperl-dev libargon2-dev cpanminus gettext clang llvm lcov +sudo apt-get upgrade -y + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..e79ee380 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,190 @@ +name: build +on: + - push + - pull_request + +jobs: + gcc: + name: GCC + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: source .github/ubuntu_deps.sh + - run: source .github/build.sh + - uses: codecov/codecov-action@v5 + with: + name: ${{ github.job }} + - uses: actions/upload-artifact@v4 + with: + name: codecov debug results ${{ github.job }} + path: "/tmp/codecov.*.gz" + + tarball: + name: Tarball + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: source .github/ubuntu_deps.sh + - run: | + date=$(date +%Y-%m-%d) + ./make-tarball.sh --nightly znc-git-$date /tmp/znc-git-$date.tar.gz + tar xvf /tmp/znc-git-$date.tar.gz + cd znc-git-$date + export CFGFLAGS="--with-gtest=$GITHUB_WORKSPACE/third_party/googletest/googletest --with-gmock=$GITHUB_WORKSPACE/third_party/googletest/googlemock --disable-swig" + source $GITHUB_WORKSPACE/.github/build.sh + - uses: actions/upload-artifact@v4 + with: + name: znc-tarball + path: /tmp/znc-git*.tar.gz + if-no-files-found: error + - uses: codecov/codecov-action@v5 + with: + name: ${{ github.job }} + + # can be removed when asan below is fixed + clang: + name: Clang + runs-on: ubuntu-24.04 + env: + CXX: clang++ + CC: clang + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: source .github/ubuntu_deps.sh + - run: source .github/build.sh + - uses: codecov/codecov-action@v5 + with: + name: ${{ github.job }} + - uses: actions/upload-artifact@v4 + with: + name: codecov debug results ${{ github.job }} + path: "/tmp/codecov.*.gz" + + + #clang_asan: + #name: Clang ASAN + #runs-on: ubuntu-24.04 + #env: + #CXX: clang++ + #CC: clang + #CXXFLAGS: "-fsanitize=address -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fPIE" + #LDFLAGS: "-fsanitize=address -pie" + #steps: + #- uses: actions/checkout@v4 + #with: + #submodules: true + #- run: source .github/ubuntu_deps.sh + #- run: source .github/build.sh + #- uses: codecov/codecov-action@v5 + + #clang_tsan: + #name: Clang TSAN + #runs-on: ubuntu-24.04 + #env: + #CXX: clang++ + #CC: clang + #CXXFLAGS: "-fsanitize=thread -O1 -fPIE" + #LDFLAGS: "-fsanitize=thread" + #steps: + #- uses: actions/checkout@v4 + #with: + #submodules: true + #- run: source .github/ubuntu_deps.sh + #- run: source .github/build.sh + #- uses: codecov/codecov-action@v5 + + # TODO: enable + #CXXFLAGS: "-fsanitize=memory -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize-memory-track-origins" + #LDFLAGS: "-fsanitize=memory" + + #CXXFLAGS: "-fsanitize=undefined -O1 -fPIE -fno-sanitize-recover" + #LDFLAGS: "-fsanitize=undefined -pie -fno-sanitize-recover" + + macos: + name: macOS + runs-on: macos-latest + env: + # This doesn't make big difference, since it's the same compiler, but we also use this variable to select lcov vs llvm-cov + CXX: clang++ + CC: clang + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: | + brew update + brew install icu4c qt6 gettext pkg-config cpanminus boost argon2 swig + - run: source .github/build.sh + env: + # https://github.com/znc/znc/issues/1937 + GTEST_FILTER: "-*Modpython*:LanguagesTests/AllLanguages.ServerDependentCapInModule/1" + - uses: codecov/codecov-action@v5 + + docker: + name: Docker push + runs-on: ubuntu-latest + needs: + - gcc + - tarball + - clang + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - id: tagger + run: | + git fetch --unshallow + echo "::set-output name=describe::$(git describe)" + if [[ "$GITHUB_REF" == refs/heads/master ]]; then + echo "::set-output name=latest::type=raw,latest" + fi + - uses: docker/metadata-action@v5 + id: meta + with: + images: zncbouncer/znc-git + tags: | + type=ref,event=branch + type=ref,event=branch,suffix=-${{steps.tagger.outputs.describe}} + ${{steps.tagger.outputs.latest}} + - run: echo "${GITHUB_REF#refs/heads/}-${{steps.tagger.outputs.describe}}" > .nightly + - run: cat .nightly + - uses: docker/login-action@v3 + if: ${{ github.repository == 'znc/znc' && github.event_name == 'push' }} + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - uses: docker/build-push-action@v6 + with: + context: . + push: ${{ github.repository == 'znc/znc' && github.event_name == 'push' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION_EXTRA=+docker-git- + + docs: + name: Docs push + runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: sudo apt-get update + - run: sudo apt-get install -y doxygen graphviz python3-yaml + - run: echo "$KEY" > ~/znc-github-key + env: + KEY: ${{ secrets.SSH_GITHUB_KEY_FOR_CI_BOT }} + - run: chmod 0600 ~/znc-github-key + - run: mkdir -p ~/.ssh + - run: cp .ci/ssh-config ~/.ssh/config + # It's not travis anymore, but oh well. TODO: fix + - run: git config --global user.email "travis-ci@znc.in" + - run: git config --global user.name "znc-travis" + - run: .ci/generate-docs.sh diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml new file mode 100644 index 00000000..de6b7c4f --- /dev/null +++ b/.github/workflows/cifuzz.yml @@ -0,0 +1,27 @@ +# Fuzzer is configured at https://github.com/google/oss-fuzz/tree/master/projects/znc +name: CIFuzz +on: [pull_request] +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'znc' + dry-run: false + language: c++ + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'znc' + fuzz-seconds: 300 + dry-run: false + language: c++ + - name: Upload Crash + uses: actions/upload-artifact@v4 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..5b72f88b --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,48 @@ +name: "CodeQL" + +on: + push: + branches: [ 'master', '1.9.x' ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ 'master' ] + schedule: + - cron: '1 14 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-24.04 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp', 'javascript', 'python' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: true + - run: | + source .github/ubuntu_deps.sh + sudo apt-get remove -y qt6-base-dev + sudo apt-get install -y qtbase5-dev + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + + - run: source .github/build.sh + env: + ZNC_QT_VER: "5" + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.gitmodules b/.gitmodules index 548e642f..e21f1e3b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,12 @@ [submodule "Csocket"] path = third_party/Csocket - url = https://github.com/jimloco/Csocket.git + url = https://github.com/znc/Csocket [submodule "third_party/googletest"] path = third_party/googletest url = https://github.com/google/googletest +[submodule "docker"] + path = docker + url = https://github.com/znc/znc-docker +[submodule "third_party/cctz"] + path = third_party/cctz + url = https://github.com/google/cctz diff --git a/.travis-coverity-scan.py b/.travis-coverity-scan.py index ce96bb6d..e8e70a95 100755 --- a/.travis-coverity-scan.py +++ b/.travis-coverity-scan.py @@ -29,3 +29,4 @@ with open('.travis.yml', 'w') as f: subprocess.check_call(['git checkout -b coverity_scan'], shell=True) subprocess.check_call(['git commit .travis.yml -m"Automatic coverity scan for {}"'.format(datetime.date.today())], shell=True) subprocess.check_call(['git push coverity coverity_scan -f'], shell=True) +subprocess.call(['git push coverity master'], shell=True) diff --git a/.travis-coverity.yml b/.travis-coverity.yml index fbb3ad66..c9dd1daf 100644 --- a/.travis-coverity.yml +++ b/.travis-coverity.yml @@ -11,11 +11,11 @@ addons: name: "znc/coverity" description: "Build submitted via Travis CI" notification_email: coverity@znc.in - build_command_prepend: "./bootstrap.sh; ./configure --enable-perl --enable-python --enable-tcl --enable-cyrus" + build_command_prepend: "./configure --enable-perl --enable-python --enable-tcl --enable-cyrus" build_command: "make VERBOSE=1" branch_pattern: coverity_scan script: - /bin/true sudo: required -dist: trusty +dist: bionic diff --git a/.travis-github.enc b/.travis-github.enc deleted file mode 100644 index 381fb565..00000000 Binary files a/.travis-github.enc and /dev/null differ diff --git a/.travis.yml b/.travis.yml index 785d24bc..c3c2f3b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,68 +1,52 @@ language: cpp -env: - global: - # SECRET_KEY, used to push docs to github and to init coverity scans - - secure: "i2f2UVDnyHT/9z0U3XvgTj8eDERvnc1Wk7HpseEjb75JwGzqn/2R+RKHmoSrwK3hFgij2IMxZL19XtHFwMz9t5A/huAAKD74KMMI/QpeZEJ/sjT3CTLcE9HEVDdJOjc7dfLRxb2hZtgvx8clZIMrpeUdPhci8openff30KvXVbg=" # These linux-specific parameters could be moved into matrix.include items, but that's lots of repetition sudo: required -dist: trusty +dist: bionic +arch: amd64 +services: + - docker -# See https://github.com/google/sanitizers/issues/856 -group: deprecated-2017Q3 - -# TODO: add Linux LLVM coverage; clang 3.5.0 on ubuntu trusty is too old. matrix: fast_finish: true include: - os: linux + dist: xenial compiler: gcc - env: BUILD_TYPE=normal BUILD_WITH=cmake COVERAGE=gcov + env: BUILD_TYPE=normal + - os: linux + compiler: clang + env: BUILD_TYPE=asan + - os: linux + compiler: clang + env: BUILD_TYPE=tsan + # TODO: enable + # - os: linux + # compiler: clang + # env: BUILD_TYPE=msan + # - os: linux + # compiler: clang + # env: BUILD_TYPE=ubsan - os: linux compiler: gcc - env: BUILD_TYPE=normal BUILD_WITH=autoconf COVERAGE=gcov - - os: linux - compiler: clang - env: BUILD_TYPE=asan BUILD_WITH=cmake COVERAGE=no - - os: linux - compiler: clang - env: BUILD_TYPE=tsan BUILD_WITH=cmake COVERAGE=no + env: BUILD_TYPE=normal + arch: arm64 - os: osx - osx_image: xcode7.3 # OS X 10.11 + osx_image: xcode9.3 # macOS 10.13 compiler: clang - env: BUILD_TYPE=normal BUILD_WITH=cmake COVERAGE=llvm - - os: osx - osx_image: xcode7.3 # OS X 10.11 - compiler: clang - env: BUILD_TYPE=normal BUILD_WITH=autoconf COVERAGE=llvm - - os: osx - osx_image: xcode8.3 # macOS 10.12 - compiler: clang - env: BUILD_TYPE=normal BUILD_WITH=cmake COVERAGE=llvm - - os: osx - osx_image: xcode8.3 # macOS 10.12 - compiler: clang - env: BUILD_TYPE=normal BUILD_WITH=autoconf COVERAGE=llvm - - os: osx - osx_image: xcode9.1 # macOS 10.12 - compiler: clang - env: BUILD_TYPE=normal BUILD_WITH=cmake COVERAGE=llvm - - os: osx - osx_image: xcode9.1 # macOS 10.12 - compiler: clang - env: BUILD_TYPE=normal BUILD_WITH=autoconf COVERAGE=llvm + env: BUILD_TYPE=normal - os: linux compiler: gcc - env: BUILD_TYPE=tarball BUILD_WITH=cmake COVERAGE=gcov - - os: linux - compiler: gcc - env: BUILD_TYPE=tarball BUILD_WITH=autoconf COVERAGE=gcov + env: BUILD_TYPE=tarball - stage: deploy os: linux + env: + # SECRET_KEY, used to push docs to github and to init coverity scans + - secure: "ne14MIcNsUNKjqtgrLHJTHXCUUMKfkV/o4sm2scWYOiIl8s1Hoqnx6mPYIr8qnedIra8fsI7sWVxXLDLd/KMTN9v9WpCwc6Sf45vYtkfrS+rNOr86wOeEbgaxDTsb2UDJhtK0InhhpkipA5jrFzQuMMMEB+JgBQltKV43wmd7Yc=" before_install: install: - if [[ "$TRAVIS_REPO_SLUG" == "znc/znc" && "$TRAVIS_PULL_REQUEST" == "false" && "$TRAVIS_BRANCH" == "master" ]]; then ATTEMPT_DEPLOY=yes; else ATTEMPT_DEPLOY=no; fi - - if [[ "$ATTEMPT_DEPLOY" == "yes" ]]; then openssl aes-256-cbc -d -in .travis-github.enc -out ~/znc-github-key -k ${SECRET_KEY}; fi + - if [[ "$ATTEMPT_DEPLOY" == "yes" ]]; then openssl aes-256-cbc -d -salt -pbkdf2 -in .travis-github.enc -out ~/znc-github-key -k ${SECRET_KEY}; fi - export SECRET_KEY=no - if [[ "$ATTEMPT_DEPLOY" == "yes" ]]; then sudo apt-get update; fi - if [[ "$ATTEMPT_DEPLOY" == "yes" ]]; then sudo apt-get install -y doxygen graphviz python3-yaml; fi @@ -78,71 +62,83 @@ matrix: - if [[ "$ATTEMPT_DEPLOY" == "yes" ]]; then ./.travis-generate-docs.sh; fi - if [[ "$ATTEMPT_DEPLOY" == "yes" ]]; then ./.travis-coverity-scan.py; fi after_success: + - stage: deploy + os: linux + env: + # DOCKER_USERNAME + - secure: "kiR372QH5Srye2beHVamOVLIPeXnDipWfzvzGJEZzbpH+aXsiD+CkbtulCR+XnKpnUAXQTmEc5ts1KjI9MGlxvP1ztxW8HMDGUMF4iFAjgZO8GyAZlH5I7pMEw7D5pn3W9y1LuCW5C9IsDcWnNTJkm32D7N34lLBCTQVw68ooDk=" + # DOCKER_PASSWORD + - secure: "FMKQarGQJ/MFXnQQWEnlWMM+XItbDPgm5tzCn4k36AsAB1s1SiQ08wmy2Ys/+kRvnPN3Clpl8P2C8CoRTMJ8WCUYZVmf3HsqvsLdrODyusR5/N1y5eOKWxo+t1qN2Jzt6oIi/ofUZdn5mdzt8yif+ufxoez+2ncZDt5HoB/suHE=" + before_install: + install: + - if [[ "$TRAVIS_REPO_SLUG" == "znc/znc" && "$TRAVIS_PULL_REQUEST" == "false" ]]; then ATTEMPT_DEPLOY=yes; else ATTEMPT_DEPLOY=no; fi + - if [[ "$ATTEMPT_DEPLOY" == "yes" ]]; then echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin; fi + - export DOCKER_PASSWORD=no DOCKER_USERNAME=no + script: + - echo "$TRAVIS_BRANCH-$(git describe)" > .nightly + - docker build --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` --build-arg VCS_REF=`git rev-parse HEAD` --build-arg VERSION_EXTRA=+docker-git- -t "zncbouncer/znc-git:$TRAVIS_BRANCH" -t "zncbouncer/znc-git:$TRAVIS_BRANCH-$(git describe)" . + - if [[ "$ATTEMPT_DEPLOY" == "yes" && "$TRAVIS_BRANCH" == "master" ]]; then docker tag "zncbouncer/znc-git:$TRAVIS_BRANCH" zncbouncer/znc-git:latest; fi + - if [[ "$ATTEMPT_DEPLOY" == "yes" ]]; then docker push zncbouncer/znc-git; fi + after_success: before_install: + - python -c "import fcntl; fcntl.fcntl(1, fcntl.F_SETFL, 0)" # https://github.com/travis-ci/travis-ci/issues/8920 - "echo os: [$TRAVIS_OS_NAME] build: [$BUILD_TYPE]" - - export SECRET_KEY=no - export CFGFLAGS= MYCXXFLAGS= MYLDFLAGS= - if [[ "$BUILD_TYPE" == "tarball" ]]; then CFGFLAGS+=" --with-gtest=$TRAVIS_BUILD_DIR/third_party/googletest/googletest --with-gmock=$TRAVIS_BUILD_DIR/third_party/googletest/googlemock --disable-swig"; fi - if [[ "$BUILD_TYPE" == "asan" ]]; then MYCXXFLAGS+=" -fsanitize=address -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fPIE" MYLDFLAGS+=" -fsanitize=address -pie"; fi - if [[ "$BUILD_TYPE" == "tsan" ]]; then MYCXXFLAGS+=" -fsanitize=thread -O1 -fPIE" MYLDFLAGS+=" -fsanitize=thread"; fi - - if [[ "$BUILD_WITH" == "cmake" ]]; then CFGSUFFIX=.sh UNITTEST=unittest INTTEST=inttest; else CFGSUFFIX= UNITTEST=test INTTEST=test2; fi - - if [[ "$COVERAGE" == "gcov" ]]; then MYCXXFLAGS+=" --coverage" MYLDFLAGS+=" --coverage"; fi - - if [[ "$COVERAGE" == "llvm" ]]; then MYCXXFLAGS+=" -fprofile-instr-generate -fcoverage-mapping" MYLDFLAGS+=" -fprofile-instr-generate"; fi - # UBSan randomly crashes clang, and very often :( - # CFGFLAGS= MYCXXFLAGS="-fsanitize=undefined -O1 -fPIE -fno-sanitize-recover" MYLDFLAGS="-fsanitize=undefined -pie -fno-sanitize-recover" + - if [[ "$BUILD_TYPE" == "msan" ]]; then MYCXXFLAGS+=" -fsanitize=memory -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize-memory-track-origins" MYLDFLAGS+=" -fsanitize=memory"; fi + - if [[ "$BUILD_TYPE" == "ubsan" ]]; then MYCXXFLAGS=" -fsanitize=undefined -O1 -fPIE -fno-sanitize-recover" MYLDFLAGS="-fsanitize=undefined -pie -fno-sanitize-recover"; fi + - if [[ "$CC" == "gcc" ]]; then MYCXXFLAGS+=" --coverage" MYLDFLAGS+=" --coverage"; fi + - if [[ "$CC" == "clang" ]]; then MYCXXFLAGS+=" -fprofile-instr-generate -fcoverage-mapping" MYLDFLAGS+=" -fprofile-instr-generate"; fi install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cat /proc/cpuinfo /proc/meminfo; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then lsb_release -a; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo add-apt-repository -y ppa:teward/swig3.0; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo add-apt-repository -y ppa:beineri/opt-qt551-trusty; fi # default qt5.2 from trusty doesn't support QByteArray::toStdString() - - if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_WITH" == "cmake" ]]; then sudo add-apt-repository -y ppa:george-edison55/cmake-3.x; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y libperl-dev tcl-dev libsasl2-dev libicu-dev swig3.0 qt55base libboost-locale-dev; fi - # Clang 3.5 TSan is broken on Travis Ubuntu 14.04. Clang 3.8 seems to work, but only without -pie (https://github.com/google/sanitizers/issues/503) - - if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "tsan" ]]; then sudo apt-get install -y clang-3.8; export CC=clang-3.8 CXX=clang++-3.8; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then source /opt/qt55/bin/qt55-env.sh; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_WITH" == "cmake" ]]; then sudo apt-get install -y cmake; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then source ~/virtualenv/python3.5/bin/activate; fi # for pip3 - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export PKG_CONFIG_PATH="/opt/python/3.5/lib/pkgconfig:$PKG_CONFIG_PATH" LD_LIBRARY_PATH="/opt/python/3.5/lib:$LD_LIBRARY_PATH"; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y libperl-dev tcl-dev libsasl2-dev libicu-dev swig qtbase5-dev libboost-locale-dev python3-pip cpanminus; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y cmake; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib); fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cpanm --notest Devel::Cover::Report::Clover; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export ZNC_MODPERL_COVERAGE=1; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sw_vers; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sysctl -a | grep cpu; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sysctl -a | grep mem; fi + # Something broke in Travis brew making it impossible to update python versions (complaining about nil:NilClass), so remove preinstalled brew and install from scratch + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then rm -rf /usr/local/Cellar/* /usr/local/opt/* /usr/local/share/aclocal; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"; hash -r; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew config; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew list --versions; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install swig python3 icu4c jq openssl qt5 gettext; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" && "$BUILD_WITH" == "cmake" ]]; then brew outdated cmake || brew upgrade cmake; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install swig icu4c jq qt5 gettext python cmake openssl pkg-config; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated python || brew upgrade python; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated cmake || brew upgrade cmake; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew info --json=v1 --installed | jq .; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PKG_CONFIG_PATH="$(brew --prefix qt5)/lib/pkgconfig:$PKG_CONFIG_PATH"; fi - - pip3 install coverage + - pip3 install --user coverage - export ZNC_MODPYTHON_COVERAGE=1 - "echo pkg-config path: [$PKG_CONFIG_PATH]" script: + - set -v - if [[ "$BUILD_TYPE" == "tarball" ]]; then ./make-tarball.sh --nightly znc-git-2015-01-16 /tmp/znc-tarball.tar.gz; fi - if [[ "$BUILD_TYPE" == "tarball" ]]; then cd /tmp; tar xvf znc-tarball.tar.gz; fi - if [[ "$BUILD_TYPE" == "tarball" ]]; then cd /tmp/znc-git-2015-01-16; fi - - if [[ "$BUILD_TYPE" != "tarball" && "$BUILD_WITH" != "cmake" ]]; then ./bootstrap.sh; fi - mkdir build - cd build - - ../configure$CFGSUFFIX --enable-debug --enable-perl --enable-python --enable-tcl --enable-cyrus --enable-charset $CFGFLAGS CXXFLAGS="$CXXFLAGS $MYCXXFLAGS" LDFLAGS="$LDFLAGS $MYLDFLAGS" - - if [[ "$BUILD_WITH" == "cmake" ]]; then cmake --system-information; else cat config.log; fi + - ../configure.sh --enable-debug --enable-perl --enable-python --enable-tcl --enable-cyrus --enable-charset $CFGFLAGS CXXFLAGS="$CXXFLAGS $MYCXXFLAGS" LDFLAGS="$LDFLAGS $MYLDFLAGS" + - cmake --system-information - make VERBOSE=1 - - env LLVM_PROFILE_FILE="$PWD/unittest.profraw" make VERBOSE=1 $UNITTEST + - env LLVM_PROFILE_FILE="$PWD/unittest.profraw" make VERBOSE=1 unittest - sudo make install # TODO: use DEVEL_COVER_OPTIONS for https://metacpan.org/pod/Devel::Cover - - env LLVM_PROFILE_FILE="$PWD/inttest.profraw" ZNC_MODPERL_COVERAGE_OPTS="-db,$PWD/cover_db" make VERBOSE=1 $INTTEST + - env LLVM_PROFILE_FILE="$PWD/inttest.profraw" ZNC_MODPERL_COVERAGE_OPTS="-db,$PWD/cover_db" PYTHONWARNINGS=error make VERBOSE=1 inttest - /usr/local/bin/znc --version after_success: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ~/perl5/bin/cover --no-gcov --report=clover; fi - | - if [[ "$TRAVIS_OS_NAME" == "osx" && "$COVERAGE" == "llvm" ]]; then + if [[ "$TRAVIS_OS_NAME" == "osx" && "$CC" == "clang" ]]; then xcrun llvm-profdata merge unittest.profraw -o unittest.profdata xcrun llvm-profdata merge inttest.profraw -o inttest.profdata xcrun llvm-cov show -show-line-counts-or-regions -instr-profile=unittest.profdata test/unittest_bin > unittest-cmake-coverage.txt - xcrun llvm-cov show -show-line-counts-or-regions -instr-profile=unittest.profdata unittest > unittest-autoconf-coverage.txt xcrun llvm-cov show -show-line-counts-or-regions -instr-profile=inttest.profdata /usr/local/bin/znc > inttest-znc-coverage.txt find /usr/local/lib/znc -name '*.so' -or -name '*.bundle' | while read f; do xcrun llvm-cov show -show-line-counts-or-regions -instr-profile=inttest.profdata $f > inttest-$(basename $f)-coverage.txt; done fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 02ff01b1..348024b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,9 +14,9 @@ # limitations under the License. # -cmake_minimum_required(VERSION 3.1) -project(ZNC VERSION 1.7.0) -set(ZNC_VERSION 1.7.x) +cmake_minimum_required(VERSION 3.13) +project(ZNC VERSION 1.10.0 LANGUAGES CXX) +set(ZNC_VERSION 1.10.x) set(append_git_version true) set(alpha_version "") # e.g. "-rc1" set(VERSION_EXTRA "" CACHE STRING @@ -41,12 +41,10 @@ endfunction() list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") -include(TestCXX11) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED true) +include(TestCXX17) if(NOT CYGWIN) - # We don't want to use -std=gnu++11 instead of -std=c++11, but among other - # things, -std=c++11 on cygwin defines __STRICT_ANSI__ which makes cygwin + # We don't want to use -std=gnu++17 instead of -std=c++17, but among other + # things, -std=c++17 on cygwin defines __STRICT_ANSI__ which makes cygwin # not to compile: undefined references to strerror_r, to fdopen, to # strcasecmp, etc (their declarations in system headers are between ifdef) set(CMAKE_CXX_EXTENSIONS false) @@ -57,6 +55,7 @@ include(use_homebrew) include(GNUInstallDirs) include(CheckCXXSymbolExists) include(copy_csocket) +include(CMakePushCheckState) set(CMAKE_THREAD_PREFER_PTHREAD true) set(THREADS_PREFER_PTHREAD_FLAG true) @@ -79,9 +78,28 @@ macro(tristate_option opt help) endif() endmacro() +set(ZNC_CMAKE_FIND_DEPS "") +set(zncpubdeps) + tristate_option(OPENSSL "Support SSL") if(WANT_OPENSSL) find_package(OpenSSL ${TRISTATE_OPENSSL_REQUIRED}) + + if(OPENSSL_FOUND) + # SSL_SESSION was made opaque in OpenSSL 1.1.0; + # LibreSSL gained that function later too. + # TODO: maybe remove this check at some point, and stop supporting old + # libssl versions + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + check_cxx_symbol_exists(SSL_SESSION_get0_cipher openssl/ssl.h + HAVE_SSL_SESSION_get0_cipher) + cmake_pop_check_state() + set(ZNC_CMAKE_FIND_DEPS + "${ZNC_CMAKE_FIND_DEPS}\nfind_dependency(OpenSSL)") + list(APPEND zncpubdeps OpenSSL::SSL) + endif() endif() set(HAVE_LIBSSL "${OPENSSL_FOUND}") @@ -96,23 +114,25 @@ set(HAVE_ZLIB "${ZLIB_FOUND}") tristate_option(CYRUS "Support authentication with Cyrus") if(WANT_CYRUS) - pkg_check_modules(CYRUS libsasl2) + pkg_check_modules(CYRUS IMPORTED_TARGET libsasl2) if(NOT CYRUS_FOUND) # libsasl2.pc is missing on 2.1.25 which is on ubuntu 14.04 # next ubuntu version has 2.1.26 which has libsasl2.pc # # osx (as of El Capitan) doesn't have it either... - set(_old_cmake_required_libraries "${CMAKE_REQUIRED_LIBRARIES}") - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} -lsasl2) - # sys/types.h here is workaround for sasl 2.1.26: - # https://github.com/znc/znc/issues/1243 - # https://lists.andrew.cmu.edu/pipermail/cyrus-sasl/2012-December/002572.html - # https://cgit.cyrus.foundation/cyrus-sasl/commit/include/sasl.h?id=2f740223fa1820dd71e6ab0e50d4964760789209 - check_cxx_symbol_exists(sasl_server_init "sys/types.h;sasl/sasl.h" - CYRUS_HARDCODED) - set(CMAKE_REQUIRED_LIBRARIES "${_old_cmake_required_libraries}") + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_LIBRARIES -lsasl2) + # sys/types.h here is workaround for sasl 2.1.26: + # https://github.com/znc/znc/issues/1243 + # https://lists.andrew.cmu.edu/pipermail/cyrus-sasl/2012-December/002572.html + # https://cgit.cyrus.foundation/cyrus-sasl/commit/include/sasl.h?id=2f740223fa1820dd71e6ab0e50d4964760789209 + check_cxx_symbol_exists(sasl_server_init "sys/types.h;sasl/sasl.h" + CYRUS_HARDCODED) + cmake_pop_check_state() if(CYRUS_HARDCODED) - set(CYRUS_LDFLAGS -lsasl2) + add_library(HardcodedCyrusDep INTERFACE) + add_library(PkgConfig::CYRUS ALIAS HardcodedCyrusDep) + target_link_libraries(HardcodedCyrusDep INTERFACE sasl2) set(CYRUS_FOUND true) endif() endif() @@ -121,11 +141,21 @@ if(WANT_CYRUS) endif() endif() +tristate_option(ARGON "Store password hashes using Argon2id instead of SHA-256") +if(WANT_ARGON) + pkg_check_modules(ARGON ${TRISTATE_ARGON_REQUIRED} IMPORTED_TARGET libargon2) +endif() +set(ZNC_HAVE_ARGON "${ARGON_FOUND}") + tristate_option(ICU "Support character encodings") if(WANT_ICU) - pkg_check_modules(ICU ${TRISTATE_ICU_REQUIRED} icu-uc) + pkg_check_modules(ICU ${TRISTATE_ICU_REQUIRED} IMPORTED_TARGET icu-uc) endif() set(HAVE_ICU "${ICU_FOUND}") +if(ICU_FOUND) + set(ZNC_CMAKE_FIND_DEPS "${ZNC_CMAKE_FIND_DEPS}\nfind_dependency_pc(ICU icu-uc)") + list(APPEND zncpubdeps PkgConfig::ICU) +endif() set(WANT_PERL false CACHE BOOL "Support Perl modules") set(WANT_PYTHON false CACHE BOOL "Support Python modules") @@ -159,7 +189,7 @@ if(WANT_PYTHON AND NOT EXISTS endif() endif() if(search_swig) - find_package(SWIG 3.0.0) + find_package(SWIG 4.0.1) if(NOT SWIG_FOUND) message(FATAL_ERROR "Can't find SWIG, therefore Perl and Python aren't supported. " @@ -171,8 +201,54 @@ if(WANT_PERL) find_package(PerlLibs 5.10 REQUIRED) endif() if (WANT_PYTHON) + set (_MIN_PYTHON_VERSION 3.4) find_package(Perl 5.10 REQUIRED) - pkg_check_modules(PYTHON "${WANT_PYTHON_VERSION}" REQUIRED) + # VERSION_GREATER_EQUAL is available only since 3.7 + if (CMAKE_VERSION VERSION_LESS 3.12) + else() + # Even if FindPython3 is available (since CMake 3.12) we still use + # pkg-config, because FindPython has a hardcoded list of python + # versions, which may become outdated when new pythons are released, + # but when cmake in the distro is old. + # + # Why FindPython3 is useful at all? Because sometimes there is no + # python3.pc, but only python-3.5.pc and python-3.6.pc; which would + # force user to provide the version explicitly via + # WANT_PYTHON_VERSION. This is the case on Gentoo when NOT building + # via emerge. + if (WANT_PYTHON_VERSION STREQUAL "python3") + find_package(Python3 COMPONENTS Development) + else() + # We used to pass value like "python-3.5" to the variable. + if (WANT_PYTHON_VERSION MATCHES "^(python-)?(.*)$") + find_package(Python3 COMPONENTS Development + EXACT "${CMAKE_MATCH_2}") + else() + message(FATAL_ERROR "Invalid value of WANT_PYTHON_VERSION") + endif() + endif() + if (Python3_FOUND AND Python3_VERSION VERSION_LESS + ${_MIN_PYTHON_VERSION}) + message(STATUS + "Python too old, need at least ${_MIN_PYTHON_VERSION}") + set(Python3_FOUND OFF) + else() + # Compatibility with pkg-config variables + set(Python3_LDFLAGS "${Python3_LIBRARIES}") + endif() + endif() + if (NOT Python3_FOUND AND WANT_PYTHON_VERSION MATCHES "^python") + # Since python 3.8, -embed is required for embedding. + pkg_check_modules(Python3 + "${WANT_PYTHON_VERSION}-embed >= ${_MIN_PYTHON_VERSION}") + if (NOT Python3_FOUND) + pkg_check_modules(Python3 + "${WANT_PYTHON_VERSION} >= ${_MIN_PYTHON_VERSION}") + endif() + endif() + if (NOT Python3_FOUND) + message(FATAL_ERROR "Python 3 is not found. Try disabling it.") + endif() endif() set(WANT_TCL false CACHE BOOL "Support Tcl modules") @@ -192,6 +268,7 @@ if(Boost_LOCALE_FOUND AND GETTEXT_MSGFMT_EXECUTABLE) set(HAVE_I18N true) else() set(HAVE_I18N false) + message(STATUS "Boost.Locale or gettext (msgfmt) is not found, disabling i18n support") endif() if(HAVE_I18N AND GETTEXT_MSGMERGE_EXECUTABLE) @@ -208,11 +285,41 @@ else() set(CSOCK_USE_POLL true) endif() +find_package(cctz QUIET) +set(cctz_cc "") +if (cctz_FOUND) + message(STATUS "Found cctz at ${cctz_DIR}") +else() + message(STATUS "Will build cctz") + set(cctz_cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/civil_time_detail.cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_fixed.cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_format.cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_if.cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_impl.cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_info.cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_libc.cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_lookup.cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/time_zone_posix.cc + ${PROJECT_SOURCE_DIR}/third_party/cctz/src/zone_info_source.cc + ) + add_library(cctz INTERFACE) + add_library(cctz::cctz ALIAS cctz) + target_include_directories(cctz INTERFACE + $) + if (APPLE) + find_library(CoreFoundation CoreFoundation REQUIRED) + target_link_libraries(cctz INTERFACE ${CoreFoundation}) + endif() +endif() + check_cxx_symbol_exists(getopt_long "getopt.h" HAVE_GETOPT_LONG) check_cxx_symbol_exists(lstat "sys/types.h;sys/stat.h;unistd.h" HAVE_LSTAT) check_cxx_symbol_exists(getpassphrase "stdlib.h" HAVE_GETPASSPHRASE) check_cxx_symbol_exists(tcsetattr "termios.h;unistd.h" HAVE_TCSETATTR) check_cxx_symbol_exists(clock_gettime "time.h" HAVE_CLOCK_GETTIME) +check_cxx_symbol_exists(gethostname "unistd.h" ZNC_HAVE_GETHOSTNAME) +check_cxx_symbol_exists(uname "sys/utsname.h" ZNC_HAVE_UNAME) # Note that old broken systems, such as OpenBSD, NetBSD, which don't support # AI_ADDRCONFIG, also have thread-unsafe getaddrinfo(). Gladly, they fixed @@ -237,12 +344,24 @@ if(append_git_version) endif() - file(GLOB csocket_files LIST_DIRECTORIES FALSE "${PROJECT_SOURCE_DIR}/third_party/Csocket/Csocket.*") if(csocket_files STREQUAL "") - message(FATAL_ERROR " It looks like git submodules are not initialized.\n" - " Run: git submodule update --init --recursive") + execute_process(COMMAND git status + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + RESULT_VARIABLE git_status_var + OUTPUT_QUIET + ERROR_QUIET) + if(git_status_var) + message(FATAL_ERROR + " It looks like git submodules are not initialized.\n" + " Either this is not a git clone, or you don't have git installed.\n" + " Fetch the tarball from the website: https://znc.in/releases/ ") + else() + message(FATAL_ERROR + " It looks like git submodules are not initialized.\n" + " Run: git submodule update --init --recursive") + endif() endif() install(DIRECTORY webskins @@ -294,6 +413,8 @@ else() endif() configure_file("include/znc/zncconfig.h.cmake.in" "include/znc/zncconfig.h") +configure_file("test/integration/znctestconfig.h.cmake.in" + "test/integration/znctestconfig.h") add_subdirectory(include) add_subdirectory(src) add_subdirectory(modules) @@ -318,6 +439,8 @@ include(CMakePackageConfigHelpers) write_basic_package_version_file("ZNCConfigVersion.cmake" COMPATIBILITY AnyNewerVersion) install(FILES + "${PROJECT_SOURCE_DIR}/cmake/CMakeFindDependencyMacroPC.cmake" + "${PROJECT_SOURCE_DIR}/cmake/use_homebrew.cmake" "${PROJECT_BINARY_DIR}/ZNCConfig.cmake" "${PROJECT_BINARY_DIR}/ZNCConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_DATADIR}/znc/cmake") @@ -344,12 +467,13 @@ summary_line("SSL " "${OPENSSL_FOUND}") summary_line("IPv6 " "${WANT_IPV6}") summary_line("Async DNS" "${HAVE_THREADED_DNS}") summary_line("Perl " "${PERLLIBS_FOUND}") -summary_line("Python " "${PYTHON_FOUND}") +summary_line("Python " "${Python3_FOUND}") summary_line("Tcl " "${TCL_FOUND}") summary_line("Cyrus " "${CYRUS_FOUND}") summary_line("Charset " "${ICU_FOUND}") summary_line("Zlib " "${ZLIB_FOUND}") summary_line("i18n " "${HAVE_I18N}") +summary_line("Argon2 " "${ZNC_HAVE_ARGON}") include(render_framed_multiline) render_framed_multiline("${summary_lines}") @@ -357,8 +481,3 @@ render_framed_multiline("${summary_lines}") message("") message("Now you can run 'make' to compile ZNC") message("") - -# TODO -# ==== -# -# remove old configure.ac and Makefile.in diff --git a/ChangeLog.md b/ChangeLog.md index be60e106..d6bf6460 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,392 @@ +# ZNC 1.9.1 (2024-07-03) + +* This is a security release to fix CVE-2024-39844: remote code execution vulnerability in modtcl. + * To mitigate this for existing installations, simply unload the modtcl module for every user, if it's loaded. Note that only users with admin rights can load modtcl at all. + * Thanks to Johannes Kuhn (DasBrain) for reporting, to glguy for the patch, and to multiple IRC network operators for help with mitigating this on server side before disclosure. +* Improve tooltips in webadmin. + + +# ZNC 1.9.0 (2024-02-22) + +## New +* Support for capability negotiation 302 and `cap-notify`. ZNC now has API `AddServerDependentCapability()`, using which modules can easily implement new capabilities: if server supports a cap, it will automatically be offered to clients which support `cap-notify` and ZNC will notify the module when the capability is enabled or disabled for server and for each client. + * Several capabilities (`away-notify`, `account-notify`, `extended-join`) were moved from the core to a new module: corecaps. + * The corecaps module is loaded automatically when upgrading from old config and when creating new config, but it's possible to unload it. + * Note: users who were using pre-release versions of 1.9.x (from git or from nightly tarballs) won't have it loaded automatically, because the existing config states `Version = 1.9`. In such case you can load it manually. This is to honor choice of users who decide to unload it, since we don't know whether the module is missing intentionally. + * Added support for `account-tag` capability, also in corecaps module. +* Updated password hashing algorithm from SHA-256 to Argon2id (if libargon2 is installed). Existing passwords are transparently upgraded upon login. +* Allow ordering of channels: via `ListChans`, `MoveChan` and `SwapChans` commands, and via webadmin. +* New user options: `DenySetIdent`, `DenySetNetwork`, `DenySetRealName`, `DenySetQuitMsg`, `DenySetCTCPReplies`. +* Switched `--makeconf` wizard default network from freenode to Libera. +* Added Portuguese and Turkish translations. +* znc-buildmod: output where the module was written to + +## Fixes +* Fixed crash when receiving SASL lines from server without having negotiated SASL via CAP. +* Fixed build with SWIG 4.2.0. +* Fixed build with LibreSSL. +* Fixed handling of timezones when parsing server-time tags received from server. +* Use module names as the module ident, otherwise some clients were merging conversations with different modules together. +* Stopped sending invalid 333 (`RPL_TOPICWHOTIME`) to client if topic owner is unknown. +* Fixed an ODR violation. +* Better hide password in PASS debug lines, sometimes it was not hidden. +* CAP REQ sent by client without CAP LS now suspends the registration as the spec requires. + +## Modules +* autoop: In some cases settings were parsed incorrectly, resulting in failure to do the autoop, now it's fixed. +* clientnotify: Added options to reduce amount of notifications depending on the IP and the client ID of the connecting client. +* controlpanel: Fixed help output. +* log: Log nickserv account in the joins lines. +* modperl: Allow overriding label for timers, which means now there can be more than 1 timer per module. +* modpython: + * Rewrote internals of how modpython loads modules. + * Main motivation for the switch from using `imp` to using `importlib` was to support Python 3.12+. + * As an additional benefit, now it's possible to structure the module as a python package (a subdirectory with `__init__.py` and other .py files). + * All the old python modules should load as they were before. + * ZNC no longer supports loading a C python extension directly through modpython (though I doubt there were any users of that obscure feature): if you want to some parts of the module to be compiled, you can always import that from `__init__.py`. + * Implemented `Module.AddCommand()` +* route_replies: + * Added Solanum-specific 337 (`RPL_WHOISTEXT`) to possible replies of `/whois`. + * Route replies to `/topic`. +* sasl: Don't forward 908 (`RPL_SASLMECHS`) to clients. +* webadmin: Fixed order of breadcrumbs in network page. +* watch: Allow new entries to use spaces. + +## Notes for package maintainers +* Require C++17 compiler. That is, GCC 8+ or Clang 5+. +* Removed autoconf, leaving only CMake as the build system. The `configure` script is now merely a wrapper for CMake, and accepts mostly the same parameters as the old `configure`. You can use either `configure` as before, or CMake directly. Minimum supported CMake version is 3.13. +* If cctz library is available on the system, it will be used, otherwise the bundled copy will be used. +* libargon2 is new optional dependency. +* Dropped support for Python < 3.4 +* Dropped support for SWIG < 4.0.1 +* The systemd unit now passes `--datadir=/var/lib/znc`. + +## Internal +* Switched to steady clock for cache map and for sockets to fix certain issues with leap seconds and DST. +* Made `CUser::Put...()` send to all clients instead of only networkless clients. Deprecate `CUser::PutAllUser()`. +* Setup Github Actions to replace old Travis CI setup. +* Added CIFuzz. +* Added CodeQL. +* List of translators is now automatically generated from Crowdin. +* Modernized the way how CMake is used. +* Updated default SSL settings from Mozilla recommendations. +* Rewrote message parsing using `std::string_view`, improving the performance of the parser. +* Web: removed legacy xhtml syntax. +* Documented more functions. +* Made some integration tests run faster by changing ServerThrottle value in the test. + + + +# ZNC 1.8.2 (2020-07-07) + +## New +* Polish translation +* List names of translators in TRANSLATORS.md file in source, as this contribution isn't directly reflected in git log +* During --makeconf warn about listening on port 6697 too, not only about 6667 + +## Fixes +* webadmin: When confirming deletion of a network and selecting No, redirect to the edituser page instead of listusers page +* Make more client command results translateable, which were missed before + + + +# ZNC 1.8.1 (2020-05-07) + +Fixed bug introduced in ZNC 1.8.0: + +Authenticated users can trigger an application crash (with a NULL pointer dereference) if echo-message is not enabled and there is no network. CVE-2020-13775 + + + +# ZNC 1.8.0 (2020-05-01) + +## New +* Output of various commands (e.g. `/znc help`) was switched from a table to a list +* Support IP while verifying SSL certificates +* Make it more visible that admins have lots of privileges + +## Fixes +* Fix parsing of channel modes when the last parameter starts with a colon, improving compatibility with InspIRCd v3 +* Fix null dereference on startup when reading invalid config +* Don't show server passwords on ZNC startup +* Fix build with newer OpenSSL +* Fix in-source CMake build +* Fix echo-message for `status` + +## Modules +* controlpanel: Add already supported NoTrafficTimeout User variable to help output +* modpython: + * Use FindPython3 in addition to pkg-config in CMake to simplify builds on Gentoo when not using emerge + * Support python 3.9 +* modtcl: Added GetNetworkName +* partyline: Module is removed +* q: Module is removed +* route_replies: Handle more numerics +* sasl: Fix sending of long authentication information +* shell: Unblock signals when spawning child processes +* simple_away: Convert to UTC time +* watch: Better support multiple clients +* webadmin: Better wording for TrustPKI setting + +## Internal +* Refactor the way how SSL certificate is checked to simplify future socket-related refactors +* Build integration test and ZNC itself with the same compiler (https://bugs.gentoo.org/699258) +* Various improvements for translation CI +* Normalize variable name sUserName/sUsername +* Make de-escaping less lenient + + + +# ZNC 1.7.5 (2019-09-23) + +* modpython: Add support for Python 3.8 +* modtcl: install .tcl files when building with CMake +* nickserv: report success of Clear commands +* Update translations, add Italian, Bulgarian, fix name of Dutch +* Update error messages to be clearer +* Add a deprecation warning to ./configure to use CMake instead in addition to an already existing warning in README + + + +# ZNC 1.7.4 (2019-06-19) + +## Fixes +* This is a security release to fix CVE-2019-12816 (remote code execution by existing non-admin users). Thanks to Jeriko One for the bugreport. +* Send "Connected!" messages to client to the correct nick. + +# Internal +* Increase znc-buildmod timeout in the test. + + + +# ZNC 1.7.3 (2019-03-30) + +## Fixes +This is a security release to fix CVE-2019-9917. Thanks to LunarBNC for the bugreport. + +## New +Docker only: the znc image now supports --user option of docker run. + + + +# ZNC 1.7.2 (2019-01-19) + +## New +* Add French translation +* Update translations + +## Fixes +* Fix compilation without deprecated APIs in OpenSSL +* Distinguish Channel CTCP Requests and Replies +* admindebug: Enforce need of TTY to turn on debug mode +* controlpanel: Add missing return to ListNetMods +* webadmin: Fix adding the last allowed network + +## Internal +* Add more details to DNS error logs + + + +# ZNC 1.7.1 (2018-07-17) + +## Security critical fixes +* CVE-2018-14055: non-admin user could gain admin privileges and shell access by injecting values into znc.conf. +* CVE-2018-14056: path traversal in HTTP handler via ../ in a web skin name. + +## Core +* Fix znc-buildmod to not hardcode the compiler used to build ZNC anymore in CMake build +* Fix language selector. Russian and German were both not selectable. +* Fix build without SSL support +* Fix several broken strings +* Stop spamming users about debug mode. This feature was added in 1.7.0, now reverted. + +## New +* Add partial Spanish, Indonesian, and Dutch translations + +## Modules +* adminlog: Log the error message again (regression of 1.7.0) +* admindebug: New module, which allows admins to turn on/off --debug in runtime +* flooddetach: Fix description of commands +* modperl: Fix memory leak in NV handling +* modperl: Fix functions which return VCString +* modpython: Fix functions which return VCString +* webadmin: Fix fancy CTCP replies editor for Firefox. It was showing the plain version even when JS is enabled + +## Internal +* Deprecate one of the overloads of CMessage::GetParams(), rename it to CMessage::GetParamsColon() +* Don't throw from destructor in the integration test +* Fix a warning with integration test / gmake / znc-buildmod interaction. + + + +# ZNC 1.7.0 (2018-05-01) + +## New +* Add CMake build. Minimum supported CMake version is 3.1. For now ZNC can be built with either CMake or autoconf. In future autoconf is going to be removed. + * Currently `znc-buildmod` requires python if CMake was used; if that's a concern for you, please open a bug. +* Increase minimum GCC version from 4.7 to 4.8. Minimum Clang version stays at 3.2. +* Make ZNC UI translateable to different languages (only with CMake), add partial Russian and German translations. + * If you want to translate ZNC to your language, please join https://crowdin.com/project/znc-bouncer +* Configs written before ZNC 0.206 can't be read anymore +* Implement IRCv3.2 capabilities `away-notify`, `account-notify`, `extended-join` +* Implement IRCv3.2 capabilities `echo-message`, `cap-notify` on the "client side" +* Update capability names as they are named in IRCv3.2: `znc.in/server-time-iso`→`server-time`, `znc.in/batch`→`batch`. Old names will continue working for a while, then will be removed in some future version. +* Make ZNC request `server-time` from server when available +* Increase accepted line length from 1024 to 2048 to give some space to message tags +* Separate buffer size settings for channels and queries +* Support separate `SSLKeyFile` and `SSLDHParamFile` configuration in addition to existing `SSLCertFile` +* Add "AuthOnlyViaModule" global/user setting +* Added pyeval module +* Added stripcontrols module +* Add new substitutions to ExpandString: `%empty%` and `%network%`. +* Stop defaulting real name to "Got ZNC?" +* Make the user aware that debug mode is enabled. +* Added `ClearAllBuffers` command +* Don't require CSRF token for POSTs if the request uses HTTP Basic auth. +* Set `HttpOnly` and `SameSite=strict` for session cookies +* Add SNI SSL client support +* Add support for CIDR notation in allowed hosts list and in trusted proxy list +* Add network-specific config for cert validation in addition to user-supplied fingerprints: `TrustAllCerts`, defaults to false, and `TrustPKI`, defaults to true. +* Add `/attach` command for symmetry with `/detach`. Unlike `/join` it allows wildcards. +* Timestamp format now supports sub-second precision with `%f`. Used in awaystore, listsockets, log modules and buffer playback when client doesn't support server-time +* Build on macOS using ICU, Python, and OpenSSL from Homebrew, if available +* Remove `--with-openssl=/path` option from ./configure. SSL is still supported and is still configurable + +## Fixes +* Revert tables to how they were in ZNC 1.4 +* Remove flawed Add/Del/ListBindHost(s). They didn't correctly do what they were intended for, but users often confused them with the SetBindHost option. SetBindHost still works. +* Fix disconnection issues when being behind NAT by decreasing the interval how often PING is sent and making it configurable via a setting to change ping timeout time +* Change default flood rates to match RFC1459, prevent excess flood problems +* Match channel names and hostmasks case-insensitively in autoattach, autocycle, autoop, autovoice, log, watch modules +* Fix crash in shell module which happens if client disconnects at a wrong time +* Decrease CPU usage when joining channels during startup or reconnect, add config write delay setting +* Always send the users name in NOTICE when logging in. +* Don't try to quit multiple times +* Don't send PART to client which sent QUIT +* Send failed logins to NOTICE instead of PRIVMSG +* Stop creating files with odd permissions on Solaris +* Save channel key on JOIN even if user was not on the channel yet +* Stop buffering and echoing CTCP requests and responses to other clients with self-message, except for /me +* Support discovery of tcl 8.6 during `./configure` + +## Modules +* adminlog: + * Make path configurable +* alias: + * Add `Dump` command to copy your config between users +* awaystore: + * Add `-chans` option which records channel highlights +* blockmotd: + * Add `GetMotd` command +* clearbufferonmsg: + * Add options which events trigger clearation of buffers. +* controlpanel: + * Add the `DelServer` command. + * Add `$user` and `$network` aliases for `$me` and `$net` respectively + * Allow reseting channel-specific `AutoClearChanBuffer` and `BufferSize` settings by setting them to `-` + * Change type of values from "double" to "number", which is more obvious for non-programmers +* crypt: + * Fix build with LibreSSL + * Cover notices, actions and topics + * Don't use the same or overlapping NickPrefix as StatusPrefix + * Add DH1080 key exchange + * Add Get/SetNickPrefix commands, hide the internal keyword from ListKeys +* cyrusauth: + * Improve UI +* fail2ban: + * Make timeout and attempts configurable, add BAN, UNBAN and LIST commands +* flooddetach: + * Detach on nick floods +* keepnick: + * Improve behaviour by listening to ircd-side numeric errors +* log: + * Add `-timestamp` option + * Add options to hide joins, quits and nick changes. + * Stop forcing username and network name to be lower case in filenames + * Log user quit messages +* missingmotd: + * Include nick in IRC numeric 422 command, reduce client confusion +* modperl: + * Provide `operator ""` for `ZNC::String` + * Honor `PERL5LIB` env var + * Fix functions like `HasPerm()` which accept `char` + * When a broken module couldn't be loaded, it couldn't be loaded anymore even if it was fixed later. + * Force strings to UTF-8 in modperl to fix double encoding during concatenation/interpolation. +* modpython: + * Require ZNC to be built with encodings support + * Disable legacy encoding mode when modpython is loaded. + * Support `CQuery` and `CServer` +* nickserv: + * Use `/nickserv identify` by default instead of `/msg nickserv`. + * Support messages from X3 services +* notify_connect: + * Show client identification +* sasl: + * Add web interface + * Enable all known mechanisms by default + * Make the first requirement for SET actually mandatory, return information about settings if no input for SET +* schat: + * Require explicit path to certificate. +* simple_away: + * Use ExpandString for away reason, rename old `%s` to `%awaytime%` + * Add `MinClients` option +* stickychan: + * Save registry on every stick/unstick action, auto-save if channel key changes + * Stop checking so often, increase delay to once every 3 minutes +* webadmin: + * Make server editor and CTCP replies editor more fancy, when JS is enabled + * Make tables sortable. + * Allow reseting chan buffer size by entering an empty value + * Show per-network traffic info + * Make the traffic info page visible for non-admins, non-admins can see only their traffic + +## Internal +* Stop pretending that ZNC ABI is stable, when it's not. Make module version checks more strict and prevent crashes when loading a module which are built for the wrong ZNC version. +* Add an integration test +* Various HTML changes +* Introduce a CMessage class and its subclasses +* Add module callbacks which accept CMessage, deprecate old callbacks +* Add `OnNumericMessage` module callback, which previously was possible only with `OnRaw`, which could give unexpected results if the message has IRCv3.2 tags. +* Modernize code to use more C++11 features +* Various code cleanups +* Fix CSS of `_default_` skin for Fingerprints section +* Add `OnUserQuitMessage()` module hook. +* Add `OnPrivBufferStarting()` and `OnPrivBufferEnding()` hooks +* `CString::WildCmp()`: add an optional case-sensitivity argument +* Do not call `OnAddUser()` hook during ZNC startup +* Allow modules to override CSRF protection. +* Rehash now reloads only global settings +* Remove `CAP CLEAR` +* Add `CChan::GetNetwork()` +* `CUser`: add API for removing and clearing allowed hosts +* `CZNC`: add missing SSL-related getters and setters +* Add a possibility (not an "option") to disable launch after --makeconf +* Move Unix signal processing to a dedicated thread. +* Add clang-format configuration, switch tabs to spaces. +* `CString::StripControls()`: Strip background colors when we reset foreground +* Make chan modes and permissions to be char instead of unsigned char. + +## Cosmetic +* Alphabetically sort the modules we compile using autoconf/Makefile +* Alphabetically sort output of `znc --help` +* Change output during startup to be more compact +* Show new server name when reconnecting to a different server with `/znc jump` +* Hide passwords in listservers output +* Filter out ZNC passwords in output of `znc -D` +* Switch znc.in URLs to https + + + +# ZNC 1.6.6 (2018-03-05) + +* Fix use-after-free in `znc --makepem`. It was broken for a long time, but + started segfaulting only now. This is a useability fix, not a security fix, + because self-signed (or signed by a CA) certificates can be created + without using `--makepem`, and then combined into znc.pem. +* Fix build on Cygwin. + + + # ZNC 1.6.5 (2017-03-12) ## Fixes diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..fc052a5c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,53 @@ +FROM alpine:3.19 + +ARG VERSION_EXTRA="" + +ARG CMAKEFLAGS="-DVERSION_EXTRA=${VERSION_EXTRA} -DCMAKE_INSTALL_PREFIX=/opt/znc -DWANT_CYRUS=YES -DWANT_PERL=YES -DWANT_PYTHON=YES -DWANT_ARGON=YES" +ARG MAKEFLAGS="" + +LABEL org.label-schema.schema-version="1.0" +LABEL org.label-schema.vcs-url="https://github.com/znc/znc" +LABEL org.label-schema.url="https://znc.in" + +COPY . /znc-src + +RUN set -x \ + && adduser -S znc \ + && addgroup -S znc +RUN apk add --no-cache \ + argon2-libs \ + boost \ + build-base \ + ca-certificates \ + cmake \ + cyrus-sasl \ + gettext \ + icu-dev \ + icu-data-full \ + openssl-dev \ + perl \ + python3 \ + su-exec \ + tini \ + tzdata +RUN apk add --no-cache --virtual build-dependencies \ + argon2-dev \ + boost-dev \ + cyrus-sasl-dev \ + perl-dev \ + python3-dev \ + swig \ + && cd /znc-src \ + && mkdir build && cd build \ + && cmake .. ${CMAKEFLAGS} \ + && make $MAKEFLAGS \ + && make install \ + && apk del build-dependencies \ + && cd / && rm -rf /znc-src + +COPY docker/slim/entrypoint.sh / +COPY docker/*/??-*.sh docker/*/startup-sequence/??-*.sh /startup-sequence/ + +VOLUME /znc-data + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/Jenkinsfile b/Jenkinsfile index c14632f8..1d8f594b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,7 +2,10 @@ timestamps { node('freebsd') { - // freebsd 10.3 + pkg install git openjdk cmake icu pkgconf swig30 python3 boost-libs gettext-tools qt5-buildtools qt5-network qt5-qmake + // AWS EC2 AMI: FreeBSD 14.1-STABLE-amd64-20241003 UEFI-PREFERRED base UFS + // pkg install git openjdk22 cmake icu pkgconf swig python3 boost-libs gettext-tools qt6-base libargon2 + // Then fill known_hosts with https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints + // (needed for crowdin cron job in jenkins) timeout(time: 30, unit: 'MINUTES') { def wsdir = pwd() stage('Checkout') { @@ -12,7 +15,7 @@ timestamps { } dir("$wsdir/build") { stage('Build') { - sh "cmake $wsdir -DWANT_PERL=ON -DWANT_PYTHON=ON -DCMAKE_INSTALL_PREFIX=$wsdir/build/install-prefix" + sh "cmake $wsdir -DWANT_PERL=ON -DWANT_PYTHON=ON -DWANT_ARGON=ON -DCMAKE_INSTALL_PREFIX=$wsdir/build/install-prefix" sh 'make VERBOSE=1 all' } stage('Unit test') { diff --git a/Makefile.in b/Makefile.in deleted file mode 100644 index 5b2d058f..00000000 --- a/Makefile.in +++ /dev/null @@ -1,263 +0,0 @@ -SHELL := @SHELL@ - -# Support out-of-tree builds -srcdir := @srcdir@ -VPATH := @srcdir@ - -prefix := @prefix@ -exec_prefix := @exec_prefix@ -datarootdir := @datarootdir@ -bindir := @bindir@ -datadir := @datadir@ -sysconfdir := @sysconfdir@ -libdir := @libdir@ -includedir := @includedir@ -sbindir := @sbindir@ -localstatedir := @localstatedir@ -systemdsystemunitdir := @systemdsystemunitdir@ -CXX := @CXX@ -CXXFLAGS := -I$(srcdir)/include -Iinclude @CPPFLAGS@ @CXXFLAGS@ -LDFLAGS := @LDFLAGS@ -LIBS := @LIBS@ -LIBZNC := @LIBZNC@ -LIBZNCDIR:= @LIBZNCDIR@ -MODDIR := @MODDIR@ -DATADIR := @DATADIR@ -PKGCONFIGDIR := $(libdir)/pkgconfig -INSTALL := @INSTALL@ -INSTALL_PROGRAM := @INSTALL_PROGRAM@ -INSTALL_SCRIPT := @INSTALL_SCRIPT@ -INSTALL_DATA := @INSTALL_DATA@ -GIT := @GIT@ -SED := @SED@ - -GTEST_DIR := @GTEST_DIR@ -GMOCK_DIR := @GMOCK_DIR@ -ifeq "$(GTEST_DIR)" "" -GTEST_DIR := $(srcdir)/third_party/googletest/googletest -endif -ifeq "$(GMOCK_DIR)" "" -GMOCK_DIR := $(srcdir)/third_party/googletest/googlemock -endif -qt_CFLAGS := @qt_CFLAGS@ -fPIC -std=c++11 -pthread -qt_LIBS := @qt_LIBS@ -pthread - -# Force the simple internal regex engine to get consistent behavior on all platforms. -# See https://code.google.com/p/chromium/issues/detail?id=317224 for more details. -GTEST_FLAGS := -DGTEST_HAS_POSIX_RE=0 -I$(GMOCK_DIR)/include -I$(GTEST_DIR)/include -# Silence warnings about overload virtual Csock::Write(), and missing field -# initializers in both gtest and gmock -GTEST_FLAGS += -Wno-overloaded-virtual -Wno-missing-field-initializers - -LIB_SRCS := ZNCString.cpp Csocket.cpp znc.cpp IRCNetwork.cpp User.cpp IRCSock.cpp \ - Client.cpp Chan.cpp Nick.cpp Server.cpp Modules.cpp MD5.cpp Buffer.cpp Utils.cpp \ - FileUtils.cpp HTTPSock.cpp Template.cpp ClientCommand.cpp Socket.cpp SHA256.cpp \ - WebModules.cpp Listener.cpp Config.cpp ZNCDebug.cpp Threads.cpp version.cpp Query.cpp \ - SSLVerifyHost.cpp Message.cpp Translation.cpp -LIB_SRCS := $(addprefix src/,$(LIB_SRCS)) -BIN_SRCS := src/main.cpp -LIB_OBJS := $(patsubst %cpp,%o,$(LIB_SRCS)) -BIN_OBJS := $(patsubst %cpp,%o,$(BIN_SRCS)) -TESTS := StringTest ConfigTest UtilsTest ThreadTest NickTest ClientTest NetworkTest \ - MessageTest ModulesTest IRCSockTest QueryTest BufferTest UserTest -TESTS := $(addprefix test/,$(addsuffix .o,$(TESTS))) -CLEAN := znc src/*.o test/*.o core core.* .version_extra .depend modules/.depend \ - unittest $(LIBZNC) -DISTCLEAN := Makefile config.log config.status znc-buildmod include/znc/zncconfig.h \ - modules/Makefile man/Makefile znc.pc znc-uninstalled.pc test/Makefile - -CXXFLAGS += -D_MODDIR_=\"$(MODDIR)\" -D_DATADIR_=\"$(DATADIR)\" - -ifneq "$(V)" "" -VERBOSE=1 -endif -ifeq "$(VERBOSE)" "" -Q=@ -E=@echo -C=-s -else -Q= -E=@\# -C= -endif - -.PHONY: all man modules clean distclean install version_extra_recompile test -.SECONDARY: - -all: znc man modules $(LIBZNC) - @echo "" - @echo " ZNC was successfully compiled." - @echo " Use '$(MAKE) install' to install ZNC to '$(prefix)'." - -ifeq "$(LIBZNC)" "" -OBJS := $(BIN_OBJS) $(LIB_OBJS) - -znc: $(OBJS) - $(E) Linking znc... - $(Q)$(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) - -else -znc: $(BIN_OBJS) $(LIBZNC) - $(E) Linking znc... - $(Q)$(CXX) $(LDFLAGS) -o $@ $(BIN_OBJS) -L. -lznc -Wl,-rpath,$(LIBZNCDIR) $(LIBS) - -$(LIBZNC): $(LIB_OBJS) - $(E) Linking $(LIBZNC)... - $(Q)$(CXX) $(LDFLAGS) -shared -o $@ $(LIB_OBJS) $(LIBS) -Wl,--out-implib=libznc.dll.a -endif - -unittest: $(LIB_OBJS) test/gtest-all.o test/gmock-all.o test/gmock-main.o $(TESTS) - $(E) Linking unit test... - $(Q)$(CXX) $(LDFLAGS) -o $@ $(LIB_OBJS) test/gtest-all.o test/gmock-all.o test/gmock-main.o $(TESTS) $(LIBS) - -inttest: test/Integration.o test/Int-gtest-all.o test/Int-gmock-all.o test/Int-gmock-main.o - $(E) Linking integration test... - $(Q)g++ -std=c++11 -o $@ test/Integration.o test/Int-gtest-all.o test/Int-gmock-all.o test/Int-gmock-main.o $(LIBS) $(qt_LIBS) - -man: - @$(MAKE) -C man $(C) - -modules: $(LIBZNC) include/znc/Csocket.h - @$(MAKE) -C modules $(C) - -clean: - rm -rf $(CLEAN) - @$(MAKE) -C modules clean; - @$(MAKE) -C man clean - -distclean: clean - rm -rf $(DISTCLEAN) - -src/%.o: src/%.cpp Makefile include/znc/Csocket.h - @mkdir -p .depend src - $(E) Building core object $*... - $(Q)$(CXX) $(CXXFLAGS) -c -o $@ $< -MD -MF .depend/$*.dep -MT $@ - -test/%.o: test/%.cpp Makefile include/znc/Csocket.h - @mkdir -p .depend test - $(E) Building test object $*... - $(Q)$(CXX) $(CXXFLAGS) $(GTEST_FLAGS) -c -o $@ $< -MD -MF .depend/$*.test.dep -MT $@ - -test/gtest-all.o: $(GTEST_DIR)/src/gtest-all.cc Makefile - @mkdir -p .depend test - $(E) Building test object gtest-all... - $(Q)$(CXX) $(CXXFLAGS) $(GTEST_FLAGS) -I$(GTEST_DIR) -c -o $@ $< -MD -MF .depend/gtest-all.dep -MT $@ - -test/gmock-all.o: $(GMOCK_DIR)/src/gmock-all.cc Makefile - @mkdir -p .depend test - $(E) Building test object gmock-all... - $(Q)$(CXX) $(CXXFLAGS) $(GTEST_FLAGS) -I$(GMOCK_DIR) -c -o $@ $< -MD -MF .depend/gmock-all.dep -MT $@ - -test/gmock-main.o: $(GMOCK_DIR)/src/gmock_main.cc Makefile - @mkdir -p .depend test - $(E) Building test object gmock-main... - $(Q)$(CXX) $(CXXFLAGS) $(GTEST_FLAGS) -c -o $@ $< -MD -MF .depend/gmock-main.dep -MT $@ - -# Qt fails under TSAN, so CXXFLAGS/LDFLAGS can't be used. -test/Integration.o: test/integration/main.cpp Makefile - @mkdir -p .depend test - $(E) Building test object Integration... - $(Q)g++ $(qt_CFLAGS) $(GTEST_FLAGS) -c -o $@ $< -MD -MF .depend/Integration.test.dep -MT $@ '-DZNC_BIN_DIR="$(bindir)"' '-DZNC_SRC_DIR="$(realpath $(srcdir))"' -test/Int-gtest-all.o: $(GTEST_DIR)/src/gtest-all.cc Makefile - @mkdir -p .depend test - $(E) Building test object Int-gtest-all... - $(Q)g++ $(qt_CFLAGS) $(GTEST_FLAGS) -I$(GTEST_DIR) -c -o $@ $< -MD -MF .depend/Int-gtest-all.dep -MT $@ -test/Int-gmock-all.o: $(GMOCK_DIR)/src/gmock-all.cc Makefile - @mkdir -p .depend test - $(E) Building test object Int-gmock-all... - $(Q)g++ $(qt_CFLAGS) $(GTEST_FLAGS) -I$(GMOCK_DIR) -c -o $@ $< -MD -MF .depend/Int-gmock-all.dep -MT $@ -test/Int-gmock-main.o: $(GMOCK_DIR)/src/gmock_main.cc Makefile - @mkdir -p .depend test - $(E) Building test object Int-gmock-main... - $(Q)g++ $(qt_CFLAGS) $(GTEST_FLAGS) -c -o $@ $< -MD -MF .depend/Int-gmock-main.dep -MT $@ - -ifneq "THIS_IS_NOT_TARBALL" "" -# If git commit was changed since previous build, add a phony target to dependencies, forcing version.o to be recompiled -# Nightlies have pregenerated version.cpp -src/version.cpp: Makefile version.sh $(shell if [ x`cat .version_extra 2> /dev/null` != x`$(srcdir)/version.sh "$(GIT)" 3>&2 2> /dev/null` ]; then echo version_extra_recompile; fi) - @mkdir -p .depend src - $(E) Building source file version.cpp... - $(Q)WRITE_OUTPUT=yes $(srcdir)/version.sh "$(GIT)" > .version_extra 2> /dev/null - -CLEAN += src/version.cpp -endif - -CLEAN += src/Csocket.cpp include/znc/Csocket.h - -src/Csocket.cpp: third_party/Csocket/Csocket.cc - @rm -f $@ - @mkdir -p src - @sed -e 's:#include "Csocket.h":#include :' $^ > $@ -include/znc/Csocket.h: third_party/Csocket/Csocket.h - @rm -f $@ - @mkdir -p include/znc - @sed -e 's:#include "defines.h":#include :' $^ > $@ -third_party/Csocket/Csocket.h third_party/Csocket/Csocket.cc: - @echo It looks like git submodules are not initialized. Run: git submodule update --init --recursive - @exit 1 - -znc.service: znc.service.in - @sed -e "s:\@CMAKE_INSTALL_FULL_BINDIR\@:$(bindir):" $^ > $@ - -CLEAN += znc.service - -install: znc znc.service $(LIBZNC) - test -d $(DESTDIR)$(bindir) || $(INSTALL) -d $(DESTDIR)$(bindir) - test -d $(DESTDIR)$(includedir)/znc || $(INSTALL) -d $(DESTDIR)$(includedir)/znc - test -d $(DESTDIR)$(PKGCONFIGDIR) || $(INSTALL) -d $(DESTDIR)$(PKGCONFIGDIR) - test -d $(DESTDIR)$(MODDIR) || $(INSTALL) -d $(DESTDIR)$(MODDIR) - test -d $(DESTDIR)$(DATADIR) || $(INSTALL) -d $(DESTDIR)$(DATADIR) - cp -R $(srcdir)/webskins $(DESTDIR)$(DATADIR) - find $(DESTDIR)$(DATADIR)/webskins -type d -exec chmod 0755 '{}' \; - find $(DESTDIR)$(DATADIR)/webskins -type f -exec chmod 0644 '{}' \; - $(INSTALL_PROGRAM) znc $(DESTDIR)$(bindir) - $(INSTALL_SCRIPT) znc-buildmod $(DESTDIR)$(bindir) - $(INSTALL_DATA) $(srcdir)/include/znc/*.h $(DESTDIR)$(includedir)/znc - $(INSTALL_DATA) include/znc/*.h $(DESTDIR)$(includedir)/znc - $(INSTALL_DATA) znc.pc $(DESTDIR)$(PKGCONFIGDIR) - @$(MAKE) -C modules install DESTDIR=$(DESTDIR); - if test -n "$(LIBZNC)"; then \ - test -d $(DESTDIR)$(LIBZNCDIR) || $(INSTALL) -d $(DESTDIR)$(LIBZNCDIR) || exit 1 ; \ - $(INSTALL_PROGRAM) $(LIBZNC) $(DESTDIR)$(LIBZNCDIR) || exit 1 ; \ - $(INSTALL_PROGRAM) libznc.dll.a $(DESTDIR)$(libdir) || exit 1 ; \ - fi - @$(MAKE) -C man install DESTDIR=$(DESTDIR) - @HAVE_SYSTEMD_TRUE@test -d $(DESTDIR)$(systemdsystemunitdir) || $(INSTALL) -d $(DESTDIR)$(systemdsystemunitdir) - @HAVE_SYSTEMD_TRUE@$(INSTALL_DATA) $(srcdir)/znc.service $(DESTDIR)$(systemdsystemunitdir) - @echo "" - @echo "******************************************************************" - @echo " ZNC was successfully installed." - @echo " You can use '$(bindir)/znc --makeconf'" - @echo " to generate a config file." - @echo "" - @echo " If you need help with using ZNC, please visit our wiki at:" - @echo " https://znc.in" - -uninstall: - rm $(DESTDIR)$(bindir)/znc - rm $(DESTDIR)$(bindir)/znc-buildmod - rm $(DESTDIR)$(includedir)/znc/*.h - rm $(DESTDIR)$(PKGCONFIGDIR)/znc.pc - rm -rf $(DESTDIR)$(DATADIR)/webskins - if test -n "$(LIBZNC)"; then \ - rm $(DESTDIR)$(LIBZNCDIR)/$(LIBZNC) || exit 1 ; \ - rmdir $(DESTDIR)$(LIBZNCDIR) || exit 1 ; \ - fi - @$(MAKE) -C man uninstall DESTDIR=$(DESTDIR) - @if test -n "modules"; then \ - $(MAKE) -C modules uninstall DESTDIR=$(DESTDIR); \ - fi - rmdir $(DESTDIR)$(bindir) - rmdir $(DESTDIR)$(includedir)/znc - rmdir $(DESTDIR)$(PKGCONFIGDIR) - @echo "Successfully uninstalled, but empty directories were left behind" - -test: unittest - $(Q)./unittest - -# This test uses files at /lib/znc, which is less than ideal, especially from build scripts of distros. -# That's why it's a separate make target. -test2: inttest - $(Q)./inttest - --include $(wildcard .depend/*.dep) diff --git a/NOTICE b/NOTICE index c06d08d3..e08eeaa5 100644 --- a/NOTICE +++ b/NOTICE @@ -16,6 +16,7 @@ ZNC includes code from jQuery UI (http://jqueryui.com/), licensed under the MIT ZNC includes code from Selectize (http://brianreavis.github.io/selectize.js/), licensed under the Apache License 2.0. ZNC includes modified code from CMakeFindFrameworks.cmake by Kitware, Inc., licensed under BSD License. ZNC includes modified code from TestLargeFiles.cmake, licensed under Boost Software License, Version 1.0. +ZNC includes code from cctz (https://github.com/google/cctz), licensed under the Apache License 2.0. ZNC is developed by these people: diff --git a/README.md b/README.md index e06df555..5330ee9a 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,9 @@ -# [![ZNC](https://wiki.znc.in/resources/assets/wiki.png)](https://znc.in) - An advanced IRC bouncer +# [![ZNC](logo.png)](https://znc.in) - An advanced IRC bouncer -[![Travis Build Status](https://img.shields.io/travis/znc/znc/master.svg?label=linux%2Fmacos)](https://travis-ci.org/znc/znc) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/znc/znc/build.yml?branch=master&label=linux)](https://github.com/znc/znc/actions/workflows/build.yml) [![Jenkins Build Status](https://img.shields.io/jenkins/s/https/jenkins.znc.in/job/znc/job/znc/job/master.svg?label=freebsd)](https://jenkins.znc.in/job/znc/job/znc/job/master/) [![AppVeyor Build status](https://img.shields.io/appveyor/ci/DarthGandalf/znc/master.svg?label=windows)](https://ci.appveyor.com/project/DarthGandalf/znc/branch/master) -[![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=1759)](https://www.bountysource.com/trackers/1759-znc?utm_source=1759&utm_medium=shield&utm_campaign=TRACKER_BADGE) [![Coverage Status](https://img.shields.io/codecov/c/github/znc/znc.svg)](https://codecov.io/gh/znc/znc) -[![Coverity Scan Build Status](https://img.shields.io/coverity/scan/6778.svg)](https://scan.coverity.com/projects/znc-coverity) ## Table of contents @@ -26,10 +24,8 @@ Core: * GNU make * pkg-config -* GCC 4.7 or clang 3.2 -* Either of: - * autoconf and automake (but only if building from git, not from tarball) - * CMake +* GCC 8 or clang 5 +* CMake 3.13 ## Optional Requirements @@ -43,8 +39,8 @@ modperl: * SWIG if building from git modpython: -* python and its bundled libpython -* perl is required +* python 3.4+ and its bundled libpython +* perl is a build dependency * macOS: Python from Homebrew is preferred over system version * SWIG if building from git @@ -54,47 +50,37 @@ cyrusauth: Character Encodings: * To get proper character encoding and charsets install ICU (`libicu4-dev`) +I18N (UI translation): +* Boost.Locale +* gettext is a build dependency + +Argon2 password hash: +* libargon2 + ## Installing ZNC -Currently there are 2 build systems in place: CMake and `./configure`. -`./configure` will eventually be removed. -There is also `configure.sh` which should make migration to CMake easier: -it accepts the same parameters as `./configure`, -but calls CMake with CMake-style parameters. - -### Installing with CMake - Installation from source code is performed using the CMake toolchain. ```shell -cmake . +mkdir build +cd build +cmake .. make make install ``` You can use `cmake-gui` or `ccmake` for more interactiveness. +There is also `configure.sh` which should make migration to CMake easier: +it accepts the same parameters as old `./configure`, +but calls CMake with CMake-style parameters. + Note for FreeBSD users: By default base OpenSSL is selected. If you want the one from ports, use `-DOPENSSL_ROOT_DIR=/usr/local`. For troubleshooting, `cmake --system-information` will show you details. -### Installing with `./configure` - -Installation from source code is performed using the `automake` toolchain. -If you are building from git, you will need to run `./autogen.sh` first to -produce the `configure` script. - -```shell -./configure -make -make install -``` - -You can use `./configure --help` if you want to get a list of options, though -the defaults should be suiting most needs. - ## Setting up znc.conf For setting up a configuration file in `~/.znc` you can simply do @@ -152,7 +138,7 @@ These directories are also in there: - moddata - Global modules save their settings here. (e.g. webadmin saves the current skin name in here) - users - This is per-user data and mainly contains just a moddata - directory. + directory and a directory for each network configured. ## ZNC's config file @@ -188,9 +174,8 @@ Python modules are loaded through the global module ## Further information -Please visit https://znc.in/ or #znc on freenode if you still have questions: -- [freenode webchat](https://webchat.freenode.net/?nick=znc_....&channels=znc) -- [ircs://irc.freenode.net:6697/znc](ircs://irc.freenode.net:6697/znc) +Please visit https://znc.in/ or #znc on Libera.Chat if you still have questions: +- [ircs://irc.libera.chat:6697/#znc](ircs://irc.libera.chat:6697/#znc) You can get the latest development version with git: `git clone https://github.com/znc/znc.git --recursive` diff --git a/TRANSLATORS.md b/TRANSLATORS.md new file mode 100644 index 00000000..74f0da3c --- /dev/null +++ b/TRANSLATORS.md @@ -0,0 +1,52 @@ +These people helped translating ZNC to various languages: + +* Alcahest ([X] Alcahest) +* Altay +* aycanuAydemir +* bashgeek (Daniel) +* CaPaCuL +* casmo (Casper) +* ChaosEngine (Andrzej Pauli) +* cirinho (Ciro Moniz) +* CJSStryker +* Danit +* DarthGandalf +* dgw +* Dreiundachzig +* Dremski +* eggoez (Baguz Ach) +* eleanorsilly (ellie is not coding in the slightest) +* freonesuka (Андрей Вальтер) +* gremax +* hypech +* JakaMedia (Jaka Media Teknologi) +* Jay2k1 +* kloun (Victor Kukshiev) +* kumanoff +* leon-th (Leon T.) +* LiteHell +* lorenzosu +* M0onshadow (Maelan) +* MikkelDK +* mkgeeky (mkgeeky) +* moonlightzzz (moonlightz) +* natinaum (natinaum) +* PauloHeaven (Paul) +* peterstanke (MAGIC) +* psychon +* remhaze +* shillos5 (Nicholas Kyriakides) +* simos (filippo.cortigiani) +* sukien +* SunOS +* tojestzart (tojestzart) +* Un1matr1x (Falk Seidel) +* unavailable (style) +* Vimart +* Wollino +* Xaris_ (Xaris) +* xAtlas (Atlas) +* Xinayder (Alexandre Oliveira) +* Zarthus (Jos Ahrens) + +Generated from https://crowdin.com/project/znc-bouncer diff --git a/ZNCConfig.cmake.in b/ZNCConfig.cmake.in index c6108e49..660895bd 100644 --- a/ZNCConfig.cmake.in +++ b/ZNCConfig.cmake.in @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +14,13 @@ # limitations under the License. # -include("${CMAKE_CURRENT_LIST_DIR}/znc.cmake") +include(CMakeFindDependencyMacro) +include("${CMAKE_CURRENT_LIST_DIR}/CMakeFindDependencyMacroPC.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/use_homebrew.cmake") +@ZNC_CMAKE_FIND_DEPS@ + +include("${CMAKE_CURRENT_LIST_DIR}/znc_internal.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/znc_public.cmake") include(CMakeParseArguments) # For some reason cygwin fails to build modules if Threads::Threads @@ -36,16 +42,12 @@ endif() function(znc_setup_module) cmake_parse_arguments(znc_mod "" "TARGET;NAME" "" ${ARGN}) set_target_properties("${znc_mod_TARGET}" PROPERTIES - CXX_STANDARD 11 - CXX_STANDARD_REQUIRED true OUTPUT_NAME "${znc_mod_NAME}" PREFIX "" SUFFIX ".so" NO_SONAME true - CXX_VISIBILITY_PRESET "hidden" - COMPILE_DEFINITIONS "znc_export_lib_EXPORTS") - set(znc_link "@znc_link@") - target_link_libraries("${znc_mod_TARGET}" PUBLIC "znc_internal_${znc_link}") + CXX_VISIBILITY_PRESET "hidden") + target_link_libraries("${znc_mod_TARGET}" PRIVATE ZNC::ZNC) endfunction() message(STATUS "Found ZNC @ZNC_VERSION@") diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index 85e17395..00000000 --- a/autogen.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh -# Run this to generate all the initial makefiles, etc. -# This is based on various examples which can be found everywhere. -set -e - -FLAGS=${FLAGS--Wall} -ACLOCAL=${ACLOCAL-aclocal} -AUTOHEADER=${AUTOHEADER-autoheader} -AUTOCONF=${AUTOCONF-autoconf} -AUTOMAKE=${AUTOMAKE-automake} -ACLOCAL_FLAGS="${ACLOCAL_FLAGS--I m4} ${FLAGS}" -AUTOHEADER_FLAGS="${AUTOHEADER_FLAGS} ${FLAGS}" -AUTOCONF_FLAGS="${AUTOCONF_FLAGS} ${FLAGS}" -AUTOMAKE_FLAGS="${AUTOMAKE_FLAGS---add-missing} ${FLAGS}" - -die() { - echo "$@" - exit 1 -} -do_cmd() { - echo "Running '$@'" - $@ -} - -test -f configure.ac || die "No configure.ac found." -which pkg-config > /dev/null || die "ERROR: pkg-config not found. Install pkg-config and run $0 again" - -# Generate aclocal.m4 for use by autoconf -do_cmd $ACLOCAL $ACLOCAL_FLAGS -# Generate zncconfig.h.in for configure -do_cmd $AUTOHEADER $AUTOHEADER_FLAGS -# Generate configure -do_cmd $AUTOCONF $AUTOCONF_FLAGS - -# Copy config.sub, config.guess, install.sh, ... -# This will complain that we don't use automake, let's just ignore that -do_cmd $AUTOMAKE $AUTOMAKE_FLAGS || true -test -f config.guess -a -f config.sub -a -f install-sh || - die "Automake didn't install config.guess, config.sub and install-sh!" - -echo "(Yes, automake is supposed to fail, ignore that)" -echo - -echo "You may now run ./configure." diff --git a/bootstrap.sh b/bootstrap.sh deleted file mode 120000 index 5347ab2e..00000000 --- a/bootstrap.sh +++ /dev/null @@ -1 +0,0 @@ -autogen.sh \ No newline at end of file diff --git a/cmake/CMakeFindDependencyMacroPC.cmake b/cmake/CMakeFindDependencyMacroPC.cmake new file mode 100644 index 00000000..9857838b --- /dev/null +++ b/cmake/CMakeFindDependencyMacroPC.cmake @@ -0,0 +1,101 @@ +# This is directly based on CMake's CMakeFindDependencyMacro from 3.26.5, the only change is that it uses pkg_check_modules instead of find_package + +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +CMakeFindDependencyMacro +------------------------ + +.. command:: find_dependency + + The ``find_dependency()`` macro wraps a :command:`find_package` call for + a package dependency:: + + find_dependency( [...]) + + It is designed to be used in a + :ref:`Package Configuration File ` + (``Config.cmake``). ``find_dependency`` forwards the correct + parameters for ``QUIET`` and ``REQUIRED`` which were passed to + the original :command:`find_package` call. Any additional arguments + specified are forwarded to :command:`find_package`. + + If the dependency could not be found it sets an informative diagnostic + message and calls :command:`return` to end processing of the calling + package configuration file and return to the :command:`find_package` + command that loaded it. + + .. note:: + + The call to :command:`return` makes this macro unsuitable to call + from :ref:`Find Modules`. + +Package Dependency Search Optimizations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If ``find_dependency`` is called with arguments identical to a previous +call in the same directory, perhaps due to diamond-shaped package +dependencies, the underlying call to :command:`find_package` is optimized +out. This optimization is important to support large package dependency +graphs while avoiding a combinatorial explosion of repeated searches. +However, the heuristic cannot account for ambient variables that +affect package behavior, such as ``_USE_STATIC_LIBS``, +offered by some packages. Therefore package configuration files should +avoid setting such variables before their calls to ``find_dependency``. + +.. versionchanged:: 3.15 + Previously, the underlying call to :command:`find_package` was always + optimized out if the package had already been found. CMake 3.15 + removed the optimization to support cases in which ``find_dependency`` + call arguments request different components. + +.. versionchanged:: 3.26 + The pre-3.15 optimization was restored, but with the above-described + heuristic to account for varying ``find_dependency`` call arguments. + +#]=======================================================================] + +macro(find_dependency_pc dep) +find_dependency(PkgConfig) + string(SHA256 cmake_fd_call_hash "${dep};${ARGN};${${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED}") + if(_CMAKE_${dep}_${cmake_fd_call_hash}_FOUND) + unset(cmake_fd_call_hash) + else() + list(APPEND _CMAKE_${dep}_HASH_STACK ${cmake_fd_call_hash}) + set(cmake_fd_quiet_arg) + if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) + set(cmake_fd_quiet_arg QUIET) + endif() + set(cmake_fd_required_arg) + if(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED) + set(cmake_fd_required_arg REQUIRED) + endif() + + get_property(cmake_fd_alreadyTransitive GLOBAL PROPERTY + _CMAKE_${dep}_TRANSITIVE_DEPENDENCY + ) + + pkg_check_modules(${dep} + ${cmake_fd_quiet_arg} + ${cmake_fd_required_arg} + IMPORTED_TARGET ${ARGN} + ) + list(POP_BACK _CMAKE_${dep}_HASH_STACK cmake_fd_call_hash) + set("_CMAKE_${dep}_${cmake_fd_call_hash}_FOUND" "${${dep}_FOUND}") + + if(NOT DEFINED cmake_fd_alreadyTransitive OR cmake_fd_alreadyTransitive) + set_property(GLOBAL PROPERTY _CMAKE_${dep}_TRANSITIVE_DEPENDENCY TRUE) + endif() + + unset(cmake_fd_alreadyTransitive) + unset(cmake_fd_call_hash) + unset(cmake_fd_quiet_arg) + unset(cmake_fd_required_arg) + if (NOT ${dep}_FOUND) + set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency ${dep} could not be found.") + set(${CMAKE_FIND_PACKAGE_NAME}_FOUND False) + return() + endif() + endif() +endmacro() diff --git a/cmake/FindPerlLibs.cmake b/cmake/FindPerlLibs.cmake index f89b31bd..2f2a8cd1 100644 --- a/cmake/FindPerlLibs.cmake +++ b/cmake/FindPerlLibs.cmake @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/TestCXX11.cmake b/cmake/TestCXX17.cmake similarity index 52% rename from cmake/TestCXX11.cmake rename to cmake/TestCXX17.cmake index 5147e2ea..795e547f 100644 --- a/cmake/TestCXX11.cmake +++ b/cmake/TestCXX17.cmake @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,26 +14,26 @@ # limitations under the License. # -if(NOT DEFINED cxx11check) - message(STATUS "Checking for C++11 support") - get_filename_component(_CXX11Check_dir "${CMAKE_CURRENT_LIST_FILE}" +if(NOT DEFINED cxx17check) + message(STATUS "Checking for C++17 support") + get_filename_component(_CXX17Check_dir "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) - try_compile(cxx11_supported - "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cxx11check" - "${_CXX11Check_dir}/cxx11check" cxx11check - OUTPUT_VARIABLE _CXX11Check_tryout) - if(cxx11_supported) - message(STATUS "Checking for C++11 support - supported") - SET(cxx11check 1 CACHE INTERNAL "C++11 support") + try_compile(cxx17_supported + "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cxx17check" + "${_CXX17Check_dir}/cxx17check" cxx17check + OUTPUT_VARIABLE _CXX17Check_tryout) + if(cxx17_supported) + message(STATUS "Checking for C++17 support - supported") + SET(cxx17check 1 CACHE INTERNAL "C++17 support") file(APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log" - "Output of C++11 check:\n${_CXX11Check_tryout}\n") + "Output of C++17 check:\n${_CXX17Check_tryout}\n") else() file(APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log" - "Error in C++11 check:\n${_CXX11Check_tryout}\n") - message(STATUS "Checking for C++11 support - not supported") + "Error in C++17 check:\n${_CXX17Check_tryout}\n") + message(STATUS "Checking for C++17 support - not supported") message(FATAL_ERROR " Upgrade your compiler.\n" - " GCC 4.7+ and Clang 3.2+ are known to work.") + " GCC 8+ and Clang 5+ should work.") endif() endif() diff --git a/cmake/copy_csocket.cmake b/cmake/copy_csocket.cmake index df48c93b..380e0359 100644 --- a/cmake/copy_csocket.cmake +++ b/cmake/copy_csocket.cmake @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/copy_csocket_cmd.cmake b/cmake/copy_csocket_cmd.cmake index fc4f250c..fa83eeb3 100644 --- a/cmake/copy_csocket_cmd.cmake +++ b/cmake/copy_csocket_cmd.cmake @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/cxx11check/CMakeLists.txt b/cmake/cxx17check/CMakeLists.txt similarity index 80% rename from cmake/cxx11check/CMakeLists.txt rename to cmake/cxx17check/CMakeLists.txt index 651dfe64..5985f6b5 100644 --- a/cmake/cxx11check/CMakeLists.txt +++ b/cmake/cxx17check/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,10 +14,10 @@ # limitations under the License. # -cmake_minimum_required(VERSION 3.0) -project(cxx11check) +cmake_minimum_required(VERSION 3.13) +project(cxx11check LANGUAGES CXX) set(CMAKE_VERBOSE_MAKEFILE true) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED true) add_executable(main main.cpp) diff --git a/cmake/cxx11check/main.cpp b/cmake/cxx17check/main.cpp similarity index 90% rename from cmake/cxx11check/main.cpp rename to cmake/cxx17check/main.cpp index 376ed5d6..3e704c31 100644 --- a/cmake/cxx11check/main.cpp +++ b/cmake/cxx17check/main.cpp @@ -10,6 +10,8 @@ // and this notice are preserved. This file is offered as-is, without any // warranty. +#include +#include template struct check { @@ -55,4 +57,9 @@ void test(); void test() { func(0); } } -int main() { return 0; } +int main() { + std::map m; + m.emplace(2, 4); + auto [x, y] = *m.begin(); + return 0; +} diff --git a/cmake/gen_version.cmake b/cmake/gen_version.cmake index e0993e59..85800d7d 100644 --- a/cmake/gen_version.cmake +++ b/cmake/gen_version.cmake @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/perl_check/CMakeLists.txt b/cmake/perl_check/CMakeLists.txt index cd4a9627..9186f9be 100644 --- a/cmake/perl_check/CMakeLists.txt +++ b/cmake/perl_check/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,8 @@ # limitations under the License. # -cmake_minimum_required(VERSION 3.0) -project(perl_check) +cmake_minimum_required(VERSION 3.13) +project(perl_check LANGUAGES CXX) set(CMAKE_VERBOSE_MAKEFILE true) if(APPLE) diff --git a/cmake/perl_check/main.cpp b/cmake/perl_check/main.cpp index 9d8137fa..fb28f971 100644 --- a/cmake/perl_check/main.cpp +++ b/cmake/perl_check/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cmake/render_framed_multiline.cmake b/cmake/render_framed_multiline.cmake index 16bebdbb..be1de9e7 100644 --- a/cmake/render_framed_multiline.cmake +++ b/cmake/render_framed_multiline.cmake @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/translation.cmake b/cmake/translation.cmake index 7eff7e2f..d827d78d 100644 --- a/cmake/translation.cmake +++ b/cmake/translation.cmake @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/translation_tmpl.py b/cmake/translation_tmpl.py index d1739c91..dd46a07a 100755 --- a/cmake/translation_tmpl.py +++ b/cmake/translation_tmpl.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/cmake/use_homebrew.cmake b/cmake/use_homebrew.cmake index aebe1f7f..d65c5438 100644 --- a/cmake/use_homebrew.cmake +++ b/cmake/use_homebrew.cmake @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -50,16 +50,21 @@ execute_process(COMMAND "${brew}" --prefix python3 if(brew_python_f EQUAL 0) find_package_message(brew_python "Python via Homebrew: ${brew_python}" "${brew_python}") - list(APPEND Python_FRAMEWORKS_ADDITIONAL + list(APPEND Python3_FRAMEWORKS_ADDITIONAL "${brew_python}/Frameworks/Python.framework") endif() -execute_process(COMMAND "${brew}" --prefix qt5 - RESULT_VARIABLE brew_qt5_f - OUTPUT_VARIABLE brew_qt5 OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) -if(brew_qt5_f EQUAL 0) - find_package_message(brew_qt5 "Qt5 via Homebrew: ${brew_qt5}" - "${brew_qt5}") +if(DEFINED ENV{ZNC_QT_VER}) + set(ZNC_QT_VER $ENV{ZNC_QT_VER}) +else() + set(ZNC_QT_VER 6) +endif() +execute_process(COMMAND "${brew}" --prefix qt${ZNC_QT_VER} + RESULT_VARIABLE brew_qt_f + OUTPUT_VARIABLE brew_qt OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) +if(brew_qt_f EQUAL 0) + find_package_message(brew_qt "Qt via Homebrew: ${brew_qt}" + "${brew_qt}") endif() execute_process(COMMAND "${brew}" --prefix gettext diff --git a/configure b/configure new file mode 100755 index 00000000..93863401 --- /dev/null +++ b/configure @@ -0,0 +1,143 @@ +#!/bin/sh +# +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# http://stackoverflow.com/questions/18993438/shebang-env-preferred-python-version +# http://stackoverflow.com/questions/12070516/conditional-shebang-line-for-different-versions-of-python +""":" +which python3 >/dev/null 2>&1 && exec python3 "$0" "$@" +which python >/dev/null 2>&1 && exec python "$0" "$@" +which python2 >/dev/null 2>&1 && exec python2 "$0" "$@" +echo "Error: configure wrapper requires python" +exec echo "Either install python, or use cmake directly" +":""" + +import argparse +import os +import shutil +import subprocess +import sys +import re + +extra_args = [os.path.dirname(sys.argv[0])] +parser = argparse.ArgumentParser() + +def gnu_install_dir(name, cmake=None): + if cmake is None: + cmake = name.upper() + parser.add_argument('--' + name, action='append', metavar=cmake, + dest='cm_args', + type=lambda s: '-DCMAKE_INSTALL_{}={}'.format(cmake, s)) + +gnu_install_dir('prefix') +gnu_install_dir('bindir') +gnu_install_dir('sbindir') +gnu_install_dir('libexecdir') +gnu_install_dir('sysconfdir') +gnu_install_dir('sharedstatedir') +gnu_install_dir('localstatedir') +gnu_install_dir('libdir') +gnu_install_dir('includedir') +gnu_install_dir('oldincludedir') +gnu_install_dir('datarootdir') +gnu_install_dir('datadir') +gnu_install_dir('infodir') +gnu_install_dir('localedir') +gnu_install_dir('mandir') +gnu_install_dir('docdir') + + +group = parser.add_mutually_exclusive_group() +group.add_argument('--enable-debug', action='store_const', dest='build_type', + const='Debug', default='Release') +group.add_argument('--disable-debug', action='store_const', dest='build_type', + const='Release', default='Release') + +def tristate(name, cmake=None): + if cmake is None: + cmake = name.upper() + group = parser.add_mutually_exclusive_group() + group.add_argument('--enable-' + name, action='append_const', + dest='cm_args', const='-DWANT_{}=YES'.format(cmake)) + group.add_argument('--disable-' + name, action='append_const', + dest='cm_args', const='-DWANT_{}=NO'.format(cmake)) + +tristate('ipv6') +tristate('openssl') +tristate('zlib') +tristate('perl') +tristate('swig') +tristate('cyrus') +tristate('charset', 'ICU') +tristate('tcl') +tristate('i18n') +tristate('argon') + +class HandlePython(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + extra_args.append('-DWANT_PYTHON=YES') + if values is not None: + extra_args.append('-DWANT_PYTHON_VERSION=' + values) + +group = parser.add_mutually_exclusive_group() +group.add_argument('--enable-python', action=HandlePython, nargs='?', + metavar='PYTHON_VERSION') +group.add_argument('--disable-python', action='append_const', dest='cm_args', + const='-DWANT_PYTHON=NO') + +parser.add_argument('--with-gtest', action='store', dest='gtest') +parser.add_argument('--with-gmock', action='store', dest='gmock') + +class HandleSystemd(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + extra_args.append('-DWANT_SYSTEMD=YES') + if values is not None: + extra_args.append('-DWANT_SYSTEMD_DIR=' + values) + +parser.add_argument('--with-systemdsystemunitdir', action=HandleSystemd, + nargs='?', metavar='UNITDIR') + +def env_type(s): + name, value = s.split('=', 1) + return (name, value) + +parser.add_argument('env', nargs='*', type=env_type, metavar='ENV_VAR=VALUE') + +args = parser.parse_args() + +if not shutil.which('cmake'): + print('CMake is required to proceed. Please install it via package manager,\n' + 'or download the binary from https://cmake.org/') + sys.exit(1) + +cm_args = args.cm_args or [] + +for env_key, env_value in args.env: + os.environ[env_key] = env_value +if args.gtest: + os.environ['GTEST_ROOT'] = args.gtest +if args.gmock: + os.environ['GMOCK_ROOT'] = args.gmock + +if os.environ.get('CXX') is not None: + extra_args.append('-DCMAKE_CXX_COMPILER=' + os.environ['CXX']) + +try: + os.remove('CMakeCache.txt') +except OSError: + pass +subprocess.check_call(['cmake', '-DCMAKE_BUILD_TYPE=' + args.build_type] + + cm_args + extra_args) diff --git a/configure.ac b/configure.ac deleted file mode 100644 index e7f79b05..00000000 --- a/configure.ac +++ /dev/null @@ -1,745 +0,0 @@ -dnl This redefines AC_PROG_CC to a version which errors out instead. This is -dnl because all our tests should be done with the C++ compiler. This should -dnl catch stuff which accidentally uses the C compiler. -AC_DEFUN([AC_PROG_CC], [m4_errprint(__file__:__line__[: Something is trying to use the C compiler. Since this is a C++ project, this should not happen! -])m4_exit(1)]) - -dnl Needed for AC_PATH_PROGS_FEATURE_CHECK which was added in 2.62 -AC_PREREQ([2.62]) -dnl Keep the version number in sync with version.h! -AC_INIT([znc], [1.7.x]) -LIBZNC_VERSION=1.7 -AC_CONFIG_MACRO_DIR([m4]) -AC_CONFIG_SRCDIR([src/znc.cpp]) -AC_LANG([C++]) -AC_CONFIG_HEADERS([include/znc/zncconfig.h]) -AH_TOP([#ifndef ZNCCONFIG_H -#define ZNCCONFIG_H]) -AH_BOTTOM([#endif /* ZNCCONFIG_H */]) - -AC_DEFUN([ZNC_AUTO_FAIL], [ - # This looks better in the summary at the end - $1="not found" - if test "x$old_$1" != "xauto" ; then - AC_MSG_ERROR([$2]) - else - AC_MSG_WARN([$3]) - fi -]) - -# AC_PROG_CXX sets CXXFLAGS to "-O2 -g" if it is unset which we don't want -CXXFLAGS="$CXXFLAGS " -AC_PROG_CXX -# "Optional" because we want custom error message -AX_CXX_COMPILE_STDCXX_11([noext], [optional]) -if test x"$HAVE_CXX11" != x1; then - AC_MSG_ERROR([Upgrade your compiler. GCC 4.7+ and Clang 3.2+ are known to work.]) -fi - -appendLib () { - if test "$LIBS" != ""; then - LIBS="$LIBS $*" - else - LIBS=$* - fi -} - -appendCXX () { - if test "$CXXFLAGS" != ""; then - CXXFLAGS="$CXXFLAGS $*" - else - CXXFLAGS=$* - fi -} - -appendMod () { - if test "$MODFLAGS" != ""; then - MODFLAGS="$MODFLAGS $*" - else - MODFLAGS=$* - fi -} - -appendLD () { - if test "$LDFLAGS" != ""; then - LDFLAGS="$LDFLAGS $*" - else - LDFLAGS=$* - fi -} - -AC_PROG_INSTALL -AC_PROG_GREP -AC_PROG_SED -AC_CANONICAL_HOST -AC_SYS_LARGEFILE -ZNC_VISIBILITY -AC_PATH_PROG([GIT], [git]) -PKG_PROG_PKG_CONFIG() - -AC_ARG_ENABLE( [debug], - AS_HELP_STRING([--enable-debug], [enable debugging]), - [DEBUG="$enableval"], - [DEBUG="no"]) -AC_ARG_ENABLE( [ipv6], - AS_HELP_STRING([--disable-ipv6], [disable ipv6 support]), - [IPV6="$enableval"], - [IPV6="yes"]) -AC_ARG_ENABLE( [openssl], - AS_HELP_STRING([--disable-openssl], [disable openssl]), - [SSL="$enableval"], - [SSL="auto"]) -AC_ARG_ENABLE( [zlib], - AS_HELP_STRING([--disable-zlib], [disable zlib]), - [ZLIB="$enableval"], - [ZLIB="auto"]) -AC_ARG_ENABLE( [perl], - AS_HELP_STRING([--enable-perl], [enable perl]), - [PERL="$enableval"], - [PERL="no"]) -AC_ARG_ENABLE( [python], - AS_HELP_STRING([--enable-python[[[=python3]]]], [enable python. - By default python3.pc of pkg-config is used, but you can use - another name, for example python-3.1]), - [PYTHON="$enableval"], - [PYTHON="no"]) -AC_ARG_ENABLE( [swig], - AS_HELP_STRING([--enable-swig], [Enable automatic generation of source files needed for modperl/modpython. - This value is ignored if perl and python are disabled. - Usually no need to enable it. - ]), - [USESWIG="$enableval"], - [USESWIG="auto"]) -AC_ARG_ENABLE( [cyrus], - AS_HELP_STRING([--enable-cyrus], [enable cyrus]), - [if test "$enableval" = "yes" ; then CYRUS=1; fi],) -AC_ARG_ENABLE( [optimization], - AS_HELP_STRING([--disable-optimization], [Disable some compiler optimizations to - decrease memory usage while compiling]), - [OPTIMIZE="$enableval"], - [OPTIMIZE="yes"]) -AC_ARG_ENABLE( [tdns], - AS_HELP_STRING([--disable-tdns], [disable threads usage for DNS resolving]), - [TDNS="$enableval"], - [TDNS="auto"]) -AC_ARG_ENABLE( [run-from-source], - AS_HELP_STRING([--enable-run-from-source], [ZNC will be runnable without installation]), - [if test "x$enableval" = "xyes" ; then - AC_DEFINE([RUN_FROM_SOURCE], [1], - [Define if ZNC should be runnable without installation]) - fi - RUNFROMSOURCE="$enableval"], - [RUNFROMSOURCE="no"]) -AC_ARG_ENABLE( [poll], - AS_HELP_STRING([--disable-poll], [use select() instead of poll()]), - [POLL="$enableval"], - [POLL="yes"]) - -AC_ARG_WITH( [gtest], - AS_HELP_STRING([--with-gtest=DIR], [Path to directory with src/gtest-all.cc file. - If not specified, git submodule will be used. - If it is not available either, "make test" will fail.])) -if test "x$with_gtest" != xno; then - AC_SUBST([GTEST_DIR], [$with_gtest]) -fi -AC_ARG_WITH([gmock], - AS_HELP_STRING([--with-gmock=DIR], [Path to directory with src/gmock-all.cc and src/gmock_main.cc files. - If not specified, git submodule will be used. - If it is not available either, "make test" will fail.])) -if test "x$with_gmock" != xno; then - AC_SUBST([GMOCK_DIR], [$with_gmock]) -fi - - -AC_ARG_WITH([systemdsystemunitdir], - AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), - [ - if test x"$with_systemdsystemunitdir" = xyes; then - with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) - fi - ]) -if test "x$with_systemdsystemunitdir" != xno; then - AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) -fi -AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ]) - -case "${host_os}" in - freebsd*) - # -D__GNU_LIBRARY__ makes this work on fbsd 4.11 - appendCXX -I/usr/local/include -D__GNU_LIBRARY__ - appendLib -L/usr/local/lib -lcompat - appendMod -L/usr/local/lib - ;; - solaris*) - appendLib -lsocket -lnsl -lresolv - ISSUN=1 - ;; - cygwin) - # We don't want to use -std=gnu++11 instead of -std=c++11, but among other things, -std=c++11 defines __STRICT_ANSI__ which makes cygwin not to compile: undefined references to strerror_r, to fdopen, to strcasecmp, etc (their declarations in system headers are between ifdef) - appendCXX -U__STRICT_ANSI__ - ISCYGWIN=1 - ;; - darwin*) - ISDARWIN=1 - - AC_PATH_PROG([BREW], [brew]) - if test -n "$BREW"; then - # add default homebrew paths - - if test "x$HAVE_ICU" != "xno"; then - AC_MSG_CHECKING([icu4c via homebrew]) - icu4c_prefix=`$BREW --prefix icu4c` - if test -n "$icu4c_prefix"; then - export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$icu4c_prefix/lib/pkgconfig" - AC_MSG_RESULT([$icu4c_prefix]) - else - AC_MSG_RESULT([no]) - fi - fi - - if test "x$PYTHON" != "xno"; then - brew_python_pc="$PYTHON" - # This is duplication of non-darwin python logic below... - if test "x$brew_python_pc" = "xyes"; then - brew_python_pc="python3" - fi - AC_MSG_CHECKING([python3 via homebrew]) - python3_prefix=`$BREW --prefix python3` - if test -n "$python3_prefix"; then - python3_prefix=`find "$python3_prefix/" -name $brew_python_pc.pc | head -n1` - if test -n "$python3_prefix"; then - python3_prefix=`dirname "$python3_prefix"` - export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$python3_prefix" - AC_MSG_RESULT([$python3_prefix]) - else - AC_MSG_RESULT([no $brew_python_pc.pc found]) - fi - else - AC_MSG_RESULT([no]) - fi - fi - - if test "x$SSL" != "xno"; then - AC_MSG_CHECKING([openssl via homebrew]) - openssl_prefix=`$BREW --prefix openssl` - if test -n "$openssl_prefix"; then - export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$openssl_prefix/lib/pkgconfig" - AC_MSG_RESULT([$openssl_prefix]) - else - AC_MSG_RESULT([no]) - fi - fi - fi - ;; -esac - -if test "$DEBUG" != "no"; then - appendCXX -ggdb3 - AC_DEFINE([_DEBUG], [1], [Define for debugging]) - if test "x$ISCYGWIN" != x1; then - # These enable some debug options in g++'s STL, e.g. invalid use of iterators - # But they cause crashes on cygwin while loading modules - AC_DEFINE([_GLIBCXX_DEBUG], [1], [Enable extra debugging checks in libstdc++]) - AC_DEFINE([_GLIBCXX_DEBUG_PEDANTIC], [1], [Enable extra debugging checks in libstdc++]) - fi -else - if test "x$OPTIMIZE" = "xyes"; then - appendCXX -O2 - - # In old times needed to define _FORTIFY_SOURCE to 2 ourself. - # Then GCC started to define it itself to 2. It was ok. - # But then GCC 4.7 started to define it to 0 or 2 depending on optimization level, and it started to conflict with our define. - AC_MSG_CHECKING([whether compiler predefines _FORTIFY_SOURCE]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - ]], [[ - #ifndef _FORTIFY_SOURCE - #error "Just checking, nothing fatal here" - #endif - ]]) - ], [ - AC_MSG_RESULT([yes]) - ], [ - AC_MSG_RESULT([no]) - appendCXX "-D_FORTIFY_SOURCE=2" - ]) - fi -fi - -if test "$IPV6" != "no"; then - AC_DEFINE([HAVE_IPV6], [1], [Define if IPv6 support is enabled]) -fi - -if test "x$GXX" = "xyes"; then - appendCXX -Wall -W -Wno-unused-parameter -Woverloaded-virtual -Wshadow -fi - -if test "$POLL" = "yes"; then - # poll() is broken on Mac OS, it fails with POLLNVAL for pipe()s. - if test -n "$ISDARWIN" - then - # Did they give us --enable-poll? - if test -n "$enable_poll" - then - # Yes, they asked for this. - AC_MSG_WARN([poll() is known to be broken on Mac OS X. You have been warned.]) - else - # No, our default value of "yes" got applied. - AC_MSG_WARN([poll() is known to be broken on Mac OS X. Using select() instead.]) - AC_MSG_WARN([Use --enable-poll for forcing poll() to be used.]) - POLL=no - fi - fi - if test "$POLL" = "yes"; then - AC_DEFINE([CSOCK_USE_POLL], [1], [Use poll() instead of select()]) - fi -fi - -AC_CHECK_LIB( gnugetopt, getopt_long,) -AC_CHECK_FUNCS([lstat getopt_long getpassphrase clock_gettime tcsetattr]) - -# ----- Check for dlopen - -AC_SEARCH_LIBS([dlopen], [dl], [], - [AC_MSG_ERROR([Could not find dlopen. ZNC will not work on this box until you upgrade this ancient system or at least install the necessary system libraries.])]) - -# ----- Check for pthreads - -AX_PTHREAD([ - AC_DEFINE([HAVE_PTHREAD], [1], [Define if you have POSIX threads libraries and header files.]) - appendCXX "$PTHREAD_CFLAGS" - appendLib "$PTHREAD_LIBS" -], [ - AC_MSG_ERROR([This compiler/OS doesn't seem to support pthreads.]) -]) - -# Note that old broken systems, such as OpenBSD, NetBSD, which don't support AI_ADDRCONFIG, also have thread-unsafe getaddrinfo(). -# Gladly, they fixed thread-safety before support of AI_ADDRCONFIG, so this can be abused to detect the thread-safe getaddrinfo(). -# -# TODO: drop support of blocking DNS at some point. OpenBSD supports AI_ADDRCONFIG since Nov 2014, and their getaddrinfo() is thread-safe since Nov 2013. NetBSD's one is thread-safe since ages ago. -DNS_TEXT=blocking -if test "x$TDNS" != "xno"; then - old_TDNS=$TDNS - AC_MSG_CHECKING([whether getaddrinfo() supports AI_ADDRCONFIG]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - #include - #include - #include - ]], [[ - int x = AI_ADDRCONFIG; - (void) x; - ]]) - ], [ - AC_MSG_RESULT([yes]) - TDNS=yes - ], [ - AC_MSG_RESULT([no]) - TDNS=no - ]) - if test "x$TDNS" = "xyes"; then - DNS_TEXT=threads - AC_DEFINE([HAVE_THREADED_DNS], [1], [Define if threaded DNS is enabled]) - else - ZNC_AUTO_FAIL([TDNS], - [support for threaded DNS not found. Try --disable-tdns. -Disabling it may result in a slight performance decrease but will not have any other side-effects], - [support for threaded DNS not found, so DNS resolving will be blocking]) - fi -fi - -# ----- Check for openssl - -SSL_TEXT="$SSL" -if test "x$SSL" != "xno"; then - old_SSL=$SSL - PKG_CHECK_MODULES([openssl], [openssl], [ - appendLib "$openssl_LIBS" - appendCXX "$openssl_CFLAGS" - ], [ - # Don't reorder this! - # On some arches libssl depends on libcrypto without linking to it :( - AC_CHECK_LIB( crypto, BIO_new,, SSL=no ; SSL_TEXT="no (libcrypt not found)" ) - AC_CHECK_LIB( ssl, SSL_shutdown,, SSL=no ; SSL_TEXT="no (libssl not found)" ) - ]) - - if test "x$SSL" != "xno"; then - AC_MSG_CHECKING([whether openssl is usable]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #include - ]], [[ - SSL_CTX* ctx = SSL_CTX_new(TLSv1_method()); - SSL* ssl = SSL_new(ctx); - DH* dh = DH_new(); - DH_free(dh); - SSL_free(ssl); - SSL_CTX_free(ctx); - ]]) - ], [ - AC_MSG_RESULT([yes]) - ], [ - AC_MSG_RESULT([no]) - SSL=no - SSL_TEXT="no (openssl not usable)" - ]) - - fi - - if test "x$SSL" = "xno" ; then - ZNC_AUTO_FAIL([SSL], - [OpenSSL not found. Try --disable-openssl.], - [OpenSSL was not found and thus disabled]) - NOSSL=1 - else - AC_DEFINE([HAVE_LIBSSL], [1], [Define if openssl is enabled]) - SSL=yes - SSL_TEXT=yes - fi -else - NOSSL=1 - SSL_TEXT="no (explicitly disabled)" -fi - -# ----- Check for zlib - -old_ZLIB="$ZLIB" -ZLIB_TEXT="$ZLIB" -if test "x$ZLIB" != "xno"; then - AC_MSG_CHECKING([whether zlib is usable]) - my_saved_LIBS="$LIBS" - appendLib "-lz" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include "zlib.h" - ]], [[ - z_stream zs; - (void) deflateInit2(&zs, 0, 0, 0, 0, 0); - (void) deflate(&zs, 0); - ]]) - ], [ - AC_MSG_RESULT([yes]) - ZLIB=yes - ZLIB_TEXT=yes - ], [ - AC_MSG_RESULT([no]) - ZLIB=no - ZLIB_TEXT="no (libz not found)" - ]) - if test "x$ZLIB" = "xno"; then - ZNC_AUTO_FAIL([ZLIB], - [zlib was not found. Try --disable-zlib], - [zlib was not found and thus disabled]) - LIBS="$my_saved_LIBS" - else - AC_DEFINE([HAVE_ZLIB], [1], [Define if zlib is available]) - fi -fi - -AC_ARG_ENABLE( [charset], - AS_HELP_STRING([--disable-charset], [disable ICU support]), - [HAVE_ICU="$enableval"], - [HAVE_ICU="auto"]) -if test "x$HAVE_ICU" != "xno" -then - old_HAVE_ICU="$HAVE_ICU" - PKG_CHECK_MODULES([icu], [icu-uc], [ - appendLib "$icu_LIBS" - appendCXX "$icu_CFLAGS" - HAVE_ICU=yes - AC_DEFINE([HAVE_ICU], [1], [Enable ICU library for Unicode handling]) - AC_DEFINE([U_USING_ICU_NAMESPACE], [0], [Do not clutter global namespace with ICU C++ stuff]) - ], [ - ZNC_AUTO_FAIL([HAVE_ICU], - [support for charset conversion not found. Try --disable-charset.], - [support for charset conversion not found and thus disabled]) - HAVE_ICU="no (icu-uc not found via pkg-config)" - ]) -fi - -# For integration test only -PKG_CHECK_MODULES([qt], [Qt5Network >= 5.4], [], [:]) - -AC_ARG_WITH( [module-prefix], - AS_HELP_STRING([--with-module-prefix], [module object code [LIBDIR/znc]]), - [MODDIR=$withval], - [MODDIR="${libdir}/znc"] ) - -AC_ARG_WITH( [module-data-prefix], - AS_HELP_STRING([--with-module-data-prefix=DIR], - [static module data (webadmin skins) [DATADIR/znc]]), - [DATADIR=$withval], - [DATADIR="${datadir}/znc"] ) - -appendMod "$CXXFLAGS" -appendMod "$CFLAG_VISIBILITY" - -if test -z "$ISSUN" -a -z "$ISDARWIN" -a -z "$ISCYGWIN"; then - # This is an unknown compiler flag on some OS - appendLD -Wl,--export-dynamic -fi - -if test -z "$ISCYGWIN" ; then - # cygwin doesn't need -fPIC, everything else does (for modules) - # warning: -fPIC ignored for target (all code is position independent) - appendMod -fPIC -else - # But cygwin does want most of ZNC in a shared lib - # See https://cygwin.com/ml/cygwin-apps/2015-07/msg00108.html for the reasoning behind the name. - LIBZNC="cygznc-${LIBZNC_VERSION}.dll" - LIBZNCDIR="$bindir" - # See above about __STRICT_ANSI__ - qt_CFLAGS="$qt_CFLAGS -U__STRICT_ANSI__" -fi - -if test -z "$ISDARWIN"; then - MODLINK="-shared" -else - # Mac OS X differentiates between shared libs (-dynamiclib) - # and loadable modules (-bundle). - MODLINK="-bundle -flat_namespace -undefined suppress" - # TODO test if -twolevel_namespace and/or - # -undefined dynamic_lookup work - # (dynamic_lookup might only work on 10.4 and later) -fi - -if test "x$PERL" != xno -o "x$PYTHON" != xno; then - old_USESWIG="$USESWIG" - if test "x$USESWIG" != "xno"; then - AC_PROG_SWIG([3.0.0]) - test -z "$SWIG" && USESWIG=no - if test "x$USESWIG" = xno -a "x$old_USESWIG" = yes; then - AC_MSG_ERROR([Could not found appropriate SWIG installation. Check config.log for details.]) - fi - fi - if test -r "$srcdir/modules/modperl/generated.tar.gz" -a -r "$srcdir/modules/modpython/generated.tar.gz"; then - AC_MSG_NOTICE([modperl/modpython files are found, disabling SWIG]) - USESWIG=no - fi - if test "x$USESWIG" = xno; then - if test ! -r "$srcdir/modules/modperl/generated.tar.gz" -o ! -r "$srcdir/modules/modpython/generated.tar.gz"; then - AC_MSG_ERROR([Can not build modperl/modpython. Either install SWIG, or build ZNC from a tarball, or disable modperl/modpython. Check config.log for details.]) - else - AC_MSG_NOTICE([modperl/modpython files are found, no SWIG needed]) - fi - USESWIG="not needed" - SWIG="" - else - USESWIG=yes - fi -else - if test "x$USESWIG" = "xyes"; then - AC_MSG_WARN([swig is used only for perl and python, but both are disabled. Disabling swig.]) - fi - USESWIG='not needed' -fi - -PERL_TEXT="$PERL" -if test "x$PERL" != "xno"; then - old_PERL="$PERL" - AC_PATH_PROG([PERL_BINARY], [perl], []) - if test -n "$PERL_BINARY" && eval "$PERL_BINARY -e'use 5.010'"; then - my_saved_LDFLAGS="$LDFLAGS" - appendLD `$PERL_BINARY -MExtUtils::Embed -e ccopts -e ldopts` - AC_CHECK_LIB(perl, perl_alloc, - [: No, we do not want autoconf to do sth automatically], - PERL="no" ; PERL_TEXT="no (libperl not found)") - LDFLAGS="$my_saved_LDFLAGS" - else - PERL="no" - PERL_TEXT="no (perl binary not found or too old)" - fi - if test "x$PERL" = "xno"; then - ZNC_AUTO_FAIL([PERL], - [perl not found. Try --disable-perl.], - [perl was not found and thus disabled]) - PERL_BINARY="" - else - PERL="yes" - PERL_TEXT="yes" - fi -fi - -PYTHON_TEXT="$PYTHON" -if test "x$PYTHON" != "xno"; then - # Default value for just --enable-python - if test "x$PYTHON" = "xyes"; then - PYTHON="python3" - fi - old_PYTHON="$PYTHON" - if test -z "$PKG_CONFIG"; then - AC_MSG_ERROR([pkg-config is required for modpython.]) - fi - PKG_CHECK_MODULES([python], [$PYTHON >= 3.0],, AC_MSG_ERROR([$PYTHON.pc not found or is wrong. Try --disable-python or install python3.])) - my_saved_LIBS="$LIBS" - my_saved_CXXFLAGS="$CXXFLAGS" - appendLib $python_LIBS - appendCXX $python_CFLAGS - AC_CHECK_FUNC([Py_Initialize], [], [PYTHON="no" ; PYTHON_TEXT="no (libpython not found)"]) - if test "x$PYTHON" != "xno"; then - # Yes, modpython depends on perl. - AC_PATH_PROG([PERL_BINARY], [perl]) - if test -z "$PERL_BINARY"; then - AC_MSG_ERROR([To compile modpython you need to be able to execute perl scripts. Try --disable-python or install perl.]) - fi - LIBS="$my_saved_LIBS" - CXXFLAGS="$my_saved_CXXFLAGS" - fi - if test "x$HAVE_ICU" != "xyes"; then - AC_MSG_ERROR([Modpython requires ZNC to be compiled with charset support, but ICU library not found. Try --disable-python or install libicu.]) - fi - if test "x$PYTHON" = "xno"; then - ZNC_AUTO_FAIL([PYTHON], - [python not found. Try --disable-python.], - [python was not found and thus disabled]) - PYTHONCFG_BINARY="" - else - PYTHON="yes" - PYTHON_TEXT="yes" - fi -fi - -if test -n "$CYRUS"; then - AC_CHECK_LIB( sasl2, sasl_server_init, - [: Dont let autoconf add -lsasl2, Makefile handles that], - AC_MSG_ERROR([could not find libsasl2. Try --disable-cyrus.])) -fi - -# Check if we want modtcl -AC_ARG_ENABLE( [tcl], - AS_HELP_STRING([--enable-tcl], [enable modtcl]), - [TCL="$enableval"], - [TCL="no"]) - -AC_ARG_WITH( [tcl-flags], - AS_HELP_STRING([--with-tcl-flags=FLAGS], - [The flags needed for compiling and linking modtcl]), - [TCL_FLAGS="$withval"],) - -if test x"$TCL" = "xyes" -then - AC_ARG_WITH( [tcl], - AS_HELP_STRING([--with-tcl=DIR], - [directory containing tclConfig.sh]), - TCL_DIR="${withval}") - - # This will need to be extended in the future, but I don't think - # it's a good idea to stuff a shitload of random stuff in here right now - for path in $TCL_DIR /usr/lib /usr/lib/tcl8.4 /usr/lib/tcl8.5 /usr/lib/tcl8.6 - do - file="${path}/tclConfig.sh" - AC_MSG_CHECKING([for ${file}]) - if test -r ${file} - then - TCL_CONF=${file} - AC_MSG_RESULT([yes]) - break - fi - AC_MSG_RESULT([no]) - done - - if test x"${TCL_CONF}" = x - then - # They --enable-tcl'd, so give them some sane default - TCL_FLAGS="-I/usr/include/tcl -ltcl" - AC_MSG_WARN([Could not find tclConfig.sh, using some sane defaults.]) - else - AC_MSG_CHECKING([modtcl flags]) - . ${TCL_CONF} - # eval because those vars depend on other vars in there - eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" - eval "TCL_INCLUDE_SPEC=\"${TCL_INCLUDE_SPEC}\"" - TCL_FLAGS="$TCL_INCLUDE_SPEC $TCL_LIB_SPEC" - AC_MSG_RESULT([$TCL_FLAGS]) - fi - my_saved_LIBS="$LIBS" - appendLib "$TCL_FLAGS" - AC_CHECK_FUNC([Tcl_CreateInterp], [TCL_TEST=yes], [TCL_TEST=no]) - if test x"$TCL_TEST" = "xno"; then - AC_MSG_ERROR([tcl not found, try --disable-tcl, or install tcl properly. If tcl is installed to a non-standard path, use --enable-tcl --with-tcl=/path]) - fi - LIBS="$my_saved_LIBS" -fi - -AC_CACHE_CHECK([for GNU make], [ac_cv_path_GNUMAKE], [ - AC_PATH_PROGS_FEATURE_CHECK([GNUMAKE], [make gmake], [[ - if $ac_path_GNUMAKE --version | $GREP GNU > /dev/null; then - ac_cv_path_GNUMAKE=$ac_path_GNUMAKE - ac_path_GNUMAKE_found=: - fi - ]], [AC_MSG_ERROR([could not find GNU make])] - ) -]) -GNUMAKE=`echo $ac_cv_path_GNUMAKE | $SED "s%.*/%%"` - -# this is in the end, for not trying to include it when it doesn't exist yet -appendCXX "-include znc/zncconfig.h" -appendMod "-include znc/zncconfig.h" - -AC_SUBST([CXXFLAGS]) -AC_SUBST([CPPFLAGS]) -AC_SUBST([MODFLAGS]) -AC_SUBST([LDFLAGS]) -AC_SUBST([LIBS]) -AC_SUBST([LIBZNC]) -AC_SUBST([LIBZNCDIR]) -AC_SUBST([ISCYGWIN]) -AC_SUBST([MODLINK]) -AC_SUBST([NOSSL]) -AC_SUBST([TCL_FLAGS]) -AC_SUBST([CYRUS]) -AC_SUBST([MODDIR]) -AC_SUBST([DATADIR]) -AC_SUBST([PERL]) -AC_SUBST([PYTHON]) -AC_SUBST([SWIG]) -AC_SUBST([python_CFLAGS]) -AC_SUBST([python_LIBS]) -AC_SUBST([qt_CFLAGS]) -AC_SUBST([qt_LIBS]) -AC_CONFIG_FILES([Makefile]) -AC_CONFIG_FILES([znc-buildmod]) -AC_CONFIG_FILES([man/Makefile]) -AC_CONFIG_FILES([znc.pc]) -AC_CONFIG_FILES([znc-uninstalled.pc]) -AC_CONFIG_FILES([modules/Makefile]) -AC_OUTPUT - -if test "x$ISCYGWIN" = x1; then - # Side effect of undefining __STRICT_ANSI__ - # http://llvm.org/bugs/show_bug.cgi?id=13530 - echo >> include/znc/zncconfig.h - echo '#ifndef ZNCCONFIG_H_ADDITIONS' >> include/znc/zncconfig.h - echo '#define ZNCCONFIG_H_ADDITIONS' >> include/znc/zncconfig.h - echo '#ifdef __clang__' >> include/znc/zncconfig.h - echo 'struct __float128;' >> include/znc/zncconfig.h - echo '#endif' >> include/znc/zncconfig.h - echo '#endif' >> include/znc/zncconfig.h -fi - -echo -echo ZNC AC_PACKAGE_VERSION configured -echo -echo "prefix: $prefix" -echo "debug: $DEBUG" -echo "ipv6: $IPV6" -echo "openssl: $SSL_TEXT" -echo "dns: $DNS_TEXT" -echo "perl: $PERL_TEXT" -echo "python: $PYTHON_TEXT" -echo "swig: $USESWIG" -if test x"$CYRUS" = "x" ; then - echo "cyrus: no" -else - echo "cyrus: yes" -fi -if test x"$TCL_FLAGS" = "x" ; then - echo "tcl: no" -else - echo "tcl: yes" -fi -echo "charset: $HAVE_ICU" -echo "zlib: $ZLIB_TEXT" -echo "run from src: $RUNFROMSOURCE" -echo -echo "Now you can run \"$GNUMAKE\" to compile ZNC" - diff --git a/configure.sh b/configure.sh deleted file mode 100755 index 097c0597..00000000 --- a/configure.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# http://stackoverflow.com/questions/18993438/shebang-env-preferred-python-version -# http://stackoverflow.com/questions/12070516/conditional-shebang-line-for-different-versions-of-python -""":" -which python3 >/dev/null 2>&1 && exec python3 "$0" "$@" -which python >/dev/null 2>&1 && exec python "$0" "$@" -which python2 >/dev/null 2>&1 && exec python2 "$0" "$@" -echo "Error: configure wrapper requires python" -exec echo "Either install python, or use cmake directly" -":""" - -import argparse -import os -import subprocess -import sys -import re - -extra_args = [os.path.dirname(sys.argv[0])] -parser = argparse.ArgumentParser() - -def gnu_install_dir(name, cmake=None): - if cmake is None: - cmake = name.upper() - parser.add_argument('--' + name, action='append', metavar=cmake, - dest='cm_args', - type=lambda s: '-DCMAKE_INSTALL_{}={}'.format(cmake, s)) - -gnu_install_dir('prefix') -gnu_install_dir('bindir') -gnu_install_dir('sbindir') -gnu_install_dir('libexecdir') -gnu_install_dir('sysconfdir') -gnu_install_dir('sharedstatedir') -gnu_install_dir('localstatedir') -gnu_install_dir('libdir') -gnu_install_dir('includedir') -gnu_install_dir('oldincludedir') -gnu_install_dir('datarootdir') -gnu_install_dir('datadir') -gnu_install_dir('infodir') -gnu_install_dir('localedir') -gnu_install_dir('mandir') -gnu_install_dir('docdir') - - -group = parser.add_mutually_exclusive_group() -group.add_argument('--enable-debug', action='store_const', dest='build_type', - const='Debug', default='Release') -group.add_argument('--disable-debug', action='store_const', dest='build_type', - const='Release', default='Release') - -def tristate(name, cmake=None): - if cmake is None: - cmake = name.upper() - group = parser.add_mutually_exclusive_group() - group.add_argument('--enable-' + name, action='append_const', - dest='cm_args', const='-DWANT_{}=YES'.format(cmake)) - group.add_argument('--disable-' + name, action='append_const', - dest='cm_args', const='-DWANT_{}=NO'.format(cmake)) - -tristate('ipv6') -tristate('openssl') -tristate('zlib') -tristate('perl') -tristate('swig') -tristate('cyrus') -tristate('charset', 'ICU') -tristate('tcl') -tristate('i18n') - -class HandlePython(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - extra_args.append('-DWANT_PYTHON=YES') - if values is not None: - extra_args.append('-DWANT_PYTHON_VERSION=' + values) - -group = parser.add_mutually_exclusive_group() -group.add_argument('--enable-python', action=HandlePython, nargs='?', - metavar='PYTHON_VERSION') -group.add_argument('--disable-python', action='append_const', dest='cm_args', - const='-DWANT_PYTHON=NO') - -parser.add_argument('--with-gtest', action='store', dest='gtest') -parser.add_argument('--with-gmock', action='store', dest='gmock') - -class HandleSystemd(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - extra_args.append('-DWANT_SYSTEMD=YES') - if values is not None: - extra_args.append('-DWANT_SYSTEMD_DIR=' + values) - -parser.add_argument('--with-systemdsystemunitdir', action=HandleSystemd, - nargs='?', metavar='UNITDIR') - -def env_type(s): - name, value = s.split('=', 1) - return (name, value) - -parser.add_argument('env', nargs='*', type=env_type, metavar='ENV_VAR=VALUE') - -args = parser.parse_args() - -cm_args = args.cm_args or [] - -for env_key, env_value in args.env: - os.environ[env_key] = env_value -if args.gtest: - os.environ['GTEST_ROOT'] = args.gtest -if args.gmock: - os.environ['GMOCK_ROOT'] = args.gmock - -if os.environ.get('CXX') is not None: - extra_args.append('-DCMAKE_CXX_COMPILER=' + os.environ['CXX']) - -try: - os.remove('CMakeCache.txt') -except OSError: - pass -subprocess.check_call(['cmake', '-DCMAKE_BUILD_TYPE=' + args.build_type] - + cm_args + extra_args) diff --git a/configure.sh b/configure.sh new file mode 120000 index 00000000..1035c697 --- /dev/null +++ b/configure.sh @@ -0,0 +1 @@ +./configure \ No newline at end of file diff --git a/docker b/docker new file mode 160000 index 00000000..8638ba43 --- /dev/null +++ b/docker @@ -0,0 +1 @@ +Subproject commit 8638ba43acaef4bff2d4d39668c1992f411230a6 diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 17003377..779ccdf6 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/include/znc/Buffer.h b/include/znc/Buffer.h index a707270f..b4cebb0f 100644 --- a/include/znc/Buffer.h +++ b/include/znc/Buffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/CMakeLists.txt b/include/znc/CMakeLists.txt index 05894ef3..0d1a24a4 100644 --- a/include/znc/CMakeLists.txt +++ b/include/znc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/include/znc/Chan.h b/include/znc/Chan.h index 35963ef8..e0fadcb4 100644 --- a/include/znc/Chan.h +++ b/include/znc/Chan.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ #include #include #include +#include #include // Forward Declarations @@ -31,7 +32,7 @@ class CConfig; class CFile; // !Forward Declarations -class CChan { +class CChan : private CCoreTranslationMixin { public: typedef enum { Voice = '+', @@ -75,10 +76,24 @@ class CChan { const CString& sHost); // Modes + /// @deprecated Use SetModes(CString, VCString) void SetModes(const CString& s); + /** + * Set the current modes for this channel + * @param sModes The mode characters being changed + * @param vsModeParams The parameters for the modes to be set + */ + void SetModes(const CString& sModes, const VCString& vsModeParams); + /// @deprecated Use ModeChange(CString, VCString, CNick*) void ModeChange(const CString& sModes, const CNick* OpNick = nullptr); - bool AddMode(unsigned char uMode, const CString& sArg); - bool RemMode(unsigned char uMode); + /** + * Handle changing the modes on a channel + * @param sModes The mode string (eg. +ovbs-pbo) + * @param vsModeParams The parameters for the mode string + */ + void ModeChange(const CString& sModes,const VCString& vsModeParams, const CNick* OpNick = nullptr); + bool AddMode(char cMode, const CString& sArg); + bool RemMode(char cMode); CString GetModeString() const; CString GetModeArg(CString& sArgs) const; CString GetModeForNames() const; @@ -120,10 +135,14 @@ class CChan { // !Buffer // m_Nick wrappers + /// e.g. '@' for chanop. CString GetPermStr() const { return m_Nick.GetPermStr(); } - bool HasPerm(unsigned char uPerm) const { return m_Nick.HasPerm(uPerm); } - bool AddPerm(unsigned char uPerm) { return m_Nick.AddPerm(uPerm); } - bool RemPerm(unsigned char uPerm) { return m_Nick.RemPerm(uPerm); } + /// e.g. '@' for chanop. + bool HasPerm(char cPerm) const { return m_Nick.HasPerm(cPerm); } + /// e.g. '@' for chanop. + bool AddPerm(char cPerm) { return m_Nick.AddPerm(cPerm); } + /// e.g. '@' for chanop. + bool RemPerm(char cPerm) { return m_Nick.RemPerm(cPerm); } // !wrappers // Setters @@ -154,14 +173,14 @@ class CChan { // Getters CIRCNetwork* GetNetwork() const { return m_pNetwork; } bool IsModeKnown() const { return m_bModeKnown; } - bool HasMode(unsigned char uMode) const; + bool HasMode(char cMode) const; CString GetOptions() const; - CString GetModeArg(unsigned char uMode) const; + CString GetModeArg(char cMode) const; std::map GetPermCounts() const; bool IsOn() const { return m_bIsOn; } const CString& GetName() const { return m_sName; } - const std::map& GetModes() const { - return m_musModes; + const std::map& GetModes() const { + return m_mcsModes; } const CString& GetKey() const { return m_sKey; } const CString& GetTopic() const { return m_sTopic; } @@ -204,7 +223,7 @@ class CChan { CBuffer m_Buffer; bool m_bModeKnown; - std::map m_musModes; + std::map m_mcsModes; }; #endif // !ZNC_CHAN_H diff --git a/include/znc/Client.h b/include/znc/Client.h index 81cd317e..e68e3920 100644 --- a/include/znc/Client.h +++ b/include/znc/Client.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ class CMessage; class CChan; // !Forward Declarations -class CAuthBase { +class CAuthBase : private CCoreTranslationMixin { public: CAuthBase(const CString& sUsername, const CString& sPassword, CZNCSock* pSock) @@ -98,71 +98,7 @@ class CClientAuth : public CAuthBase { class CClient : public CIRCSocket { public: - CClient() - : CIRCSocket(), - m_bGotPass(false), - m_bGotNick(false), - m_bGotUser(false), - m_bInCap(false), - m_bCapNotify(false), - m_bAwayNotify(false), - m_bAccountNotify(false), - m_bExtendedJoin(false), - m_bNamesx(false), - m_bUHNames(false), - m_bAway(false), - m_bServerTime(false), - m_bBatch(false), - m_bEchoMessage(false), - m_bSelfMessage(false), - m_bPlaybackActive(false), - m_pUser(nullptr), - m_pNetwork(nullptr), - m_sNick("unknown-nick"), - m_sPass(""), - m_sUser(""), - m_sNetwork(""), - m_sIdentifier(""), - m_spAuth(), - m_ssAcceptedCaps(), - m_ssSupportedTags(), - m_mCoreCaps({ - {"multi-prefix", - {false, [this](bool bVal) { m_bNamesx = bVal; }}}, - {"userhost-in-names", - {false, [this](bool bVal) { m_bUHNames = bVal; }}}, - {"echo-message", - {false, [this](bool bVal) { m_bEchoMessage = bVal; }}}, - {"server-time", - {false, [this](bool bVal) { - m_bServerTime = bVal; - SetTagSupport("time", bVal); - }}}, - {"batch", {false, [this](bool bVal) { - m_bBatch = bVal; - SetTagSupport("batch", bVal); - }}}, - {"cap-notify", - {false, [this](bool bVal) { m_bCapNotify = bVal; }}}, - {"away-notify", - {true, [this](bool bVal) { m_bAwayNotify = bVal; }}}, - {"account-notify", - {true, [this](bool bVal) { m_bAccountNotify = bVal; }}}, - {"extended-join", - {true, [this](bool bVal) { m_bExtendedJoin = bVal; }}}, - }) { - EnableReadLine(); - // RFC says a line can have 512 chars max, but we are - // a little more gentle ;) - SetMaxBufferThreshold(1024); - - // For compatibility with older clients - m_mCoreCaps["znc.in/server-time-iso"] = m_mCoreCaps["server-time"]; - m_mCoreCaps["znc.in/batch"] = m_mCoreCaps["batch"]; - m_mCoreCaps["znc.in/self-message"] = { - false, [this](bool bVal) { m_bSelfMessage = bVal; }}; - } - + CClient(); virtual ~CClient(); CClient(const CClient&) = delete; @@ -175,12 +111,15 @@ class CClient : public CIRCSocket { CString GetNick(bool bAllowIRCNick = true) const; CString GetNickMask() const; CString GetIdentifier() const { return m_sIdentifier; } + unsigned short int CapVersion() const { return m_uCapVersion; } + bool HasCap302() const { return CapVersion() >= 302; } bool HasCapNotify() const { return m_bCapNotify; } bool HasAwayNotify() const { return m_bAwayNotify; } bool HasAccountNotify() const { return m_bAccountNotify; } bool HasExtendedJoin() const { return m_bExtendedJoin; } bool HasNamesx() const { return m_bNamesx; } bool HasUHNames() const { return m_bUHNames; } + bool HasChgHost() const { return m_bChgHost; } bool IsAway() const { return m_bAway; } bool HasServerTime() const { return m_bServerTime; } bool HasBatch() const { return m_bBatch; } @@ -199,14 +138,23 @@ class CClient : public CIRCSocket { void SetPlaybackActive(bool bActive) { m_bPlaybackActive = bActive; } void PutIRC(const CString& sLine); + // Strips prefix and potentially tags before sending to server. + void PutIRCStripping(CMessage Message); /** Sends a raw data line to the client. * @param sLine The line to be sent. * - * The line is first passed \e unmodified to the \ref CModule::OnSendToClient() - * module hook. If no module halts the process, the line is then sent to the client. + * The line is first passed \e unmodified to the \ref + * CModule::OnSendToClient() module hook. If no module halts the process, + * the line is then sent to the client. * * These lines appear in the debug output in the following syntax: * \code [time] (user/network) ZNC -> CLI [line] \endcode + * + * Prefer \l PutClient() instead. + */ + bool PutClientRaw(const CString& sLine); + /** Sends a message to the client. + * See \l PutClient(const CMessage&) for details. */ void PutClient(const CString& sLine); /** Sends a message to the client. @@ -245,14 +193,17 @@ class CClient : public CIRCSocket { * ----------- | ---------- * \c time | \l CClient::HasServerTime() (server-time) * \c batch | \l CClient::HasBatch() (batch) + * + * Additional tags can be added via \l CClient::SetTagSupport(). * - * @warning Bypassing the filter may cause troubles to some older IRC clients. + * @warning Bypassing the filter may cause troubles to some older IRC + * clients. * * It is possible to bypass the filter by converting a message to a string * using \l CMessage::ToString(), and passing the resulting raw line to the - * \l CClient::PutClient(const CString& sLine) overload: + * \l CClient::PutClientRaw(const CString& sLine): * \code - * pClient->PutClient(Message.ToString()); + * pClient->PutClientRaw(Message.ToString()); * \endcode */ bool PutClient(const CMessage& Message); @@ -269,14 +220,16 @@ class CClient : public CIRCSocket { bool IsTagEnabled(const CString& sTag) const { return 1 == m_ssSupportedTags.count(sTag); } - /** Registers a tag as being supported or unsupported by a client. + /** Registers a tag as being supported or unsupported by the client. + * This doesn't affect tags which the client sends. * @param sTag The tag to register. * @param bState Whether the client supports the tag. */ void SetTagSupport(const CString& sTag, bool bState); - void NotifyServerDependentCaps(const SCString& ssCaps); - void ClearServerDependentCaps(); + /** Notifies client about one specific cap which server has just notified us about. + */ + void NotifyServerDependentCap(const CString& sCap, bool bValue, const CString& sValue); void ReadLine(const CString& sData) override; bool SendMotd(); @@ -299,11 +252,33 @@ class CClient : public CIRCSocket { CIRCSock* GetIRCSock(); CString GetFullName() const; + /** Sends AUTHENTIATE message to client. + * It encodes it to Base64 and splits to multiple IRC messages if necessary. + */ + void SendSASLChallenge(CString sMessage); + void RefuseSASLLogin(const CString& sReason); + void AcceptSASLLogin(CUser& User); + /** Start potentially asynchronous process of checking the credentials. + * When finished, will send the success/failure SASL numerics to the + * client. This is mostly useful for SASL PLAIN. + * sAuthorizationId is internally passed through ParseUser() to extract + * network and client id. + * Currently sUser should match the username from + * sAuthorizationId: either in full, or just the username part; but in a + * future version we may add an ability to actually login as a different + * user, but with your password. + */ + void StartSASLPasswordCheck(const CString& sUser, const CString& sPassword, + const CString& sAuthorizationId); + /** Gathers username, client id, network name, if present. Returns username + * cleaned from client id and network name. + */ + CString ParseUser(const CString& sAuthLine); + private: void HandleCap(const CMessage& Message); void RespondCap(const CString& sResponse); void ParsePass(const CString& sAuthLine); - void ParseUser(const CString& sAuthLine); void ParseIdentifier(const CString& sAuthLine); template @@ -315,6 +290,15 @@ class CClient : public CIRCSocket { unsigned int DetachChans(const std::set& sChans); bool OnActionMessage(CActionMessage& Message); + void OnAuthenticateMessage(const CAuthenticateMessage& Message); + void AbortSASL(const CString& sFullIRCLine); + bool IsDuringSASL() const { return !m_sSASLMechanism.empty(); } + + /** + * Returns set of all available SASL mechanisms. + */ + SCString EnumerateSASLMechanisms() const; + bool OnCTCPMessage(CCTCPMessage& Message); bool OnJoinMessage(CJoinMessage& Message); bool OnModeMessage(CModeMessage& Message); @@ -323,6 +307,7 @@ class CClient : public CIRCSocket { bool OnPingMessage(CMessage& Message); bool OnPongMessage(CMessage& Message); bool OnQuitMessage(CQuitMessage& Message); + bool OnTagMessage(CTargetMessage& Message); bool OnTextMessage(CTextMessage& Message); bool OnTopicMessage(CTopicMessage& Message); bool OnOtherMessage(CMessage& Message); @@ -331,6 +316,7 @@ class CClient : public CIRCSocket { bool m_bGotPass; bool m_bGotNick; bool m_bGotUser; + unsigned short int m_uCapVersion; bool m_bInCap; bool m_bCapNotify; bool m_bAwayNotify; @@ -338,32 +324,41 @@ class CClient : public CIRCSocket { bool m_bExtendedJoin; bool m_bNamesx; bool m_bUHNames; + bool m_bChgHost; bool m_bAway; bool m_bServerTime; bool m_bBatch; bool m_bEchoMessage; bool m_bSelfMessage; + bool m_bMessageTagCap; + bool m_bSASLCap; bool m_bPlaybackActive; CUser* m_pUser; CIRCNetwork* m_pNetwork; CString m_sNick; CString m_sPass; + // User who didn't necessarily login yet, or might not even exist. CString m_sUser; CString m_sNetwork; CString m_sIdentifier; + CString m_sSASLBuffer; + // Set while the exchange is in progress + CString m_sSASLMechanism; + // Username who successfully logged in using SASL. This is not a CUser* + // because between the 903 and CAP END the user could have been deleted. + CString m_sSASLUser; std::shared_ptr m_spAuth; SCString m_ssAcceptedCaps; SCString m_ssSupportedTags; - // The capabilities supported by the ZNC core - capability names mapped - // to a pair which contains a bool describing whether the capability is - // server-dependent, and a capability value change handler. - std::map>> - m_mCoreCaps; - // A subset of CIRCSock::GetAcceptedCaps(), the caps that can be listed - // in CAP LS and may be notified to the client with CAP NEW (cap-notify). - SCString m_ssServerDependentCaps; + SCString m_ssPreviouslyFailedSASLMechanisms; + // The capabilities supported by the ZNC core - capability names mapped to + // change handler. Note: this lists caps which don't require support on IRC + // server. + static const std::map>& + CoreCaps(); friend class ClientTest; + friend class CCoreCaps; }; #endif // !ZNC_CLIENT_H diff --git a/include/znc/Config.h b/include/znc/Config.h index bfa681f3..48d394f7 100644 --- a/include/znc/Config.h +++ b/include/znc/Config.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,10 +35,11 @@ struct CConfigEntry { class CConfig { public: - CConfig() : m_ConfigEntries(), m_SubConfigs() {} + CConfig() : m_ConfigEntries(), m_SubConfigs(), m_SubConfigNameSets() {} typedef std::map EntryMap; - typedef std::map SubConfig; + typedef std::pair SubConfigEntry; + typedef std::vector SubConfig; typedef std::map SubConfigMap; typedef EntryMap::const_iterator EntryMapIterator; @@ -62,14 +63,13 @@ class CConfig { bool AddSubConfig(const CString& sTag, const CString& sName, CConfig Config) { - SubConfig& conf = m_SubConfigs[sTag]; - SubConfig::const_iterator it = conf.find(sName); + auto& nameset = m_SubConfigNameSets[sTag]; - if (it != conf.end()) { - return false; - } + if (nameset.find(sName) != nameset.end()) return false; + + nameset.insert(sName); + m_SubConfigs[sTag].emplace_back(sName, Config); - conf[sName] = Config; return true; } @@ -142,9 +142,9 @@ class CConfig { return false; } - bool FindSubConfig(const CString& sName, SubConfig& Config, + bool FindSubConfig(const CString& sTag, SubConfig& Config, bool bErase = true) { - SubConfigMap::iterator it = m_SubConfigs.find(sName); + auto it = m_SubConfigs.find(sTag); if (it == m_SubConfigs.end()) { Config.clear(); return false; @@ -153,6 +153,7 @@ class CConfig { if (bErase) { m_SubConfigs.erase(it); + m_SubConfigNameSets.erase(sTag); } return true; @@ -166,8 +167,12 @@ class CConfig { void Write(CFile& file, unsigned int iIndentation = 0); private: + typedef SCString SubConfigNameSet; + typedef std::map SubConfigNameSetMap; + EntryMap m_ConfigEntries; SubConfigMap m_SubConfigs; + SubConfigNameSetMap m_SubConfigNameSets; }; #endif // !ZNC_CONFIG_H diff --git a/include/znc/ExecSock.h b/include/znc/ExecSock.h index f8cbc72c..7adcbffa 100644 --- a/include/znc/ExecSock.h +++ b/include/znc/ExecSock.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/FileUtils.h b/include/znc/FileUtils.h index 013c006d..73b8e699 100644 --- a/include/znc/FileUtils.h +++ b/include/znc/FileUtils.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/HTTPSock.h b/include/znc/HTTPSock.h index 2a097edc..13332c28 100644 --- a/include/znc/HTTPSock.h +++ b/include/znc/HTTPSock.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index 6057c1bb..63dc5447 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ class CIRCNetworkPingTimer; class CIRCNetworkJoinTimer; class CMessage; -class CIRCNetwork { +class CIRCNetwork : private CCoreTranslationMixin { public: static bool IsValidNetwork(const CString& sNetwork); @@ -93,6 +93,9 @@ class CIRCNetwork { bool AddChan(CChan* pChan); bool AddChan(const CString& sName, bool bInConfig); bool DelChan(const CString& sName); + bool MoveChan(const CString& sChan, unsigned int index, CString& sError); + bool SwapChans(const CString& sChan1, const CString& sChan2, + CString& sError); void JoinChans(); void JoinChans(std::set& sChans); @@ -153,8 +156,11 @@ class CIRCNetwork { void IRCConnected(); void IRCDisconnected(); void CheckIRCConnect(); + void NotifyClientsAboutServerDependentCap(const CString& sCap, bool bValue); + bool IsServerCapAccepted(const CString& sCap) const; bool PutIRC(const CString& sLine); + bool PutIRC(const CMessage& Message); // Buffers void AddRawBuffer(const CMessage& Format, const CString& sText = "") { diff --git a/include/znc/IRCSock.h b/include/znc/IRCSock.h index 470551ad..9a4ffefb 100644 --- a/include/znc/IRCSock.h +++ b/include/znc/IRCSock.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,8 +56,49 @@ class CIRCSock : public CIRCSocket { void SockError(int iErrno, const CString& sDescription) override; void Timeout() override; void ReachedMaxBuffer() override; - +#ifdef HAVE_LIBSSL + void SSLCertError(X509* pCert) override; +#endif + /** Sends a raw data line to the server. + * @param sLine The line to be sent. + * + * The line is first passed \e unmodified to the \ref + * CModule::OnSendToIRC() module hook. If no module halts the process, + * the line is then sent to the server. + * + * Prefer \l PutIRC() instead. + */ + void PutIRCRaw(const CString& sLine); + /** Sends a message to the server. + * See \l PutIRC(const CMessage&) for details. + */ void PutIRC(const CString& sLine); + /** Sends a message to the server. + * @param Message The message to be sent. + * @note Only known and compatible messages and tags are sent. + * + * This method can delay the delivery of the message to honor protection + * from flood. + * + * This method ensures that only tags, that were negotiated with CAP REQ + * and CAP ACK, are sent. Not all IRC server are capable of handling all + * messages and tags. Thus, in order to stay compatible with a variety of + * IRC servers, ZNC has to filter out messages and tags that the server + * has not explicitly acknowleged. + * + * Additional tags can be added via \l CIRCSock::SetTagSupport(). + * + * @warning Bypassing the filter may cause troubles to some older IRC + * servers. + * + * It is possible to bypass the filter by converting a message to a string + * using \l CMessage::ToString(), and passing the resulting raw line to the + * \l CIRCSock::PutIRCRaw(const CString& sLine): + * \code + * pServer->PutIRCRaw(Message.ToString()); + * \endcode + */ + void PutIRC(const CMessage& Message); void PutIRCQuick(const CString& sLine); //!< Should be used for PONG only void ResetChans(); void Quit(const CString& sQuitMsg = ""); @@ -71,6 +112,16 @@ class CIRCSock : public CIRCSocket { * should be resumed again. */ void ResumeCap(); + + bool IsTagEnabled(const CString& sTag) const { + return 1 == m_ssSupportedTags.count(sTag); + } + /** Registers a tag as being supported or unsupported by the server. + * This doesn't affect tags which the server sends. + * @param sTag The tag to register. + * @param bState Whether the client supports the tag. + */ + void SetTagSupport(const CString& sTag, bool bState); // Setters void SetPass(const CString& s) { m_sPass = s; } @@ -78,10 +129,11 @@ class CIRCSock : public CIRCSocket { // Getters unsigned int GetMaxNickLen() const { return m_uMaxNickLen; } - EChanModeArgs GetModeType(unsigned char uMode) const; - unsigned char GetPermFromMode(unsigned char uMode) const; - const std::map& GetChanModes() const { - return m_mueChanModes; + EChanModeArgs GetModeType(char cMode) const; + char GetPermFromMode(char cMode) const; + char GetModeFromPerm(char cPerm) const; + const std::map& GetChanModes() const { + return m_mceChanModes; } bool IsPermChar(const char c) const { return (c != '\0' && GetPerms().find(c) != CString::npos); @@ -101,15 +153,16 @@ class CIRCSock : public CIRCSocket { bool HasAccountNotify() const { return m_bAccountNotify; } bool HasExtendedJoin() const { return m_bExtendedJoin; } bool HasServerTime() const { return m_bServerTime; } - const std::set& GetUserModes() const { - return m_scUserModes; - } + bool HasMessageTagCap() const { return m_bMessageTagCap; } + const std::set& GetUserModes() const { return m_scUserModes; } // This is true if we are past raw 001 bool IsAuthed() const { return m_bAuthed; } const SCString& GetAcceptedCaps() const { return m_ssAcceptedCaps; } bool IsCapAccepted(const CString& sCap) { return 1 == m_ssAcceptedCaps.count(sCap); } + CString GetCapLsValue(const CString& sKey, + const CString& sDefault = "") const; const MCString& GetISupport() const { return m_mISupport; } CString GetISupport(const CString& sKey, const CString& sDefault = "") const; @@ -124,6 +177,7 @@ class CIRCSock : public CIRCSocket { bool OnActionMessage(CActionMessage& Message); bool OnAwayMessage(CMessage& Message); bool OnCapabilityMessage(CMessage& Message); + bool OnChgHostMessage(CChgHostMessage& Message); bool OnCTCPMessage(CCTCPMessage& Message); bool OnErrorMessage(CMessage& Message); bool OnInviteMessage(CMessage& Message); @@ -137,10 +191,11 @@ class CIRCSock : public CIRCSocket { bool OnPingMessage(CMessage& Message); bool OnPongMessage(CMessage& Message); bool OnQuitMessage(CQuitMessage& Message); + bool OnTagMessage(CTargetMessage& Message); bool OnTextMessage(CTextMessage& Message); bool OnTopicMessage(CTopicMessage& Message); bool OnWallopsMessage(CMessage& Message); - bool OnServerCapAvailable(const CString& sCap); + bool OnServerCapAvailable(const CString& sCap, const CString& sValue); // !Message Handlers void SetNick(const CString& sNick); @@ -158,10 +213,11 @@ class CIRCSock : public CIRCSocket { bool m_bAccountNotify; bool m_bExtendedJoin; bool m_bServerTime; + bool m_bMessageTagCap; CString m_sPerms; CString m_sPermModes; - std::set m_scUserModes; - std::map m_mueChanModes; + std::set m_scUserModes; + std::map m_mceChanModes; CIRCNetwork* m_pNetwork; CNick m_Nick; CString m_sPass; @@ -170,18 +226,22 @@ class CIRCSock : public CIRCSocket { unsigned int m_uCapPaused; SCString m_ssAcceptedCaps; SCString m_ssPendingCaps; + MCString m_msCapLsValues; time_t m_lastCTCP; unsigned int m_uNumCTCP; static const time_t m_uCTCPFloodTime; static const unsigned int m_uCTCPFloodCount; MCString m_mISupport; - std::deque m_vsSendQueue; + std::deque m_vSendQueue; short int m_iSendsAllowed; unsigned short int m_uFloodBurst; double m_fFloodRate; bool m_bFloodProtection; + SCString m_ssSupportedTags; + VCString m_vsSSLError; friend class CIRCFloodTimer; + friend class CCoreCaps; }; #endif // !ZNC_IRCSOCK_H diff --git a/include/znc/Listener.h b/include/znc/Listener.h index e42aab73..d8e43185 100644 --- a/include/znc/Listener.h +++ b/include/znc/Listener.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/Message.h b/include/znc/Message.h index 5c39cfe3..f2686f00 100644 --- a/include/znc/Message.h +++ b/include/znc/Message.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,17 +17,10 @@ #ifndef ZNC_MESSAGE_H #define ZNC_MESSAGE_H -// Remove this after Feb 2016 when Debian 7 is EOL -#if __cpp_ref_qualifiers >= 200710 -#define ZNC_LVREFQUAL & -#elif defined(__clang__) -#define ZNC_LVREFQUAL & -#elif __GNUC__ > 4 || \ - __GNUC__ == 4 && (__GNUC_MINOR__ > 8 || \ - __GNUC_MINOR__ == 8 && __GNUC_PATCHLEVEL__ >= 1) -#define ZNC_LVREFQUAL & +#ifdef SWIG +#define ZNC_MSG_DEPRECATED(msg) #else -#define ZNC_LVREFQUAL +#define ZNC_MSG_DEPRECATED(msg) __attribute__((deprecated(msg))) #endif #include @@ -55,7 +48,8 @@ class CIRCNetwork; * - `nick` is the sender, which can be obtained with GetNick() * - `cmd` is command, which is obtained via GetCommand() * - `0`, `1`, ... are parameters, available via GetParam(n), which removes the - * leading colon (:). If you don't want to remove the colon, use GetParams(). + * leading colon (:). If you don't want to remove the colon, use + * GetParamsColon(). * * For certain events, like a PRIVMSG, convienience commands like GetChan() and * GetNick() are available, this is not true for all CMessage extensions. @@ -71,8 +65,10 @@ class CMessage { Unknown, Account, Action, + Authenticate, Away, Capability, + ChgHost, CTCP, Error, Invite, @@ -86,6 +82,7 @@ class CMessage { Ping, Pong, Quit, + TagMsg, Text, Topic, Wallops, @@ -114,8 +111,27 @@ class CMessage { void SetCommand(const CString& sCommand); const VCString& GetParams() const { return m_vsParams; } - CString GetParams(unsigned int uIdx, unsigned int uLen = -1) const; + + /** + * Get a subset of the message parameters + * + * This allows accessing a vector of a specific range of parameters, + * allowing easy inline use, such as `pChan->SetModes(Message.GetParam(2), Message.GetParamsSplit(3));` + * + * @param uIdx The index of the first parameter to retrieve + * @param uLen How many parameters to retrieve + * @return A VCString containing the retrieved parameters + */ + VCString GetParamsSplit(unsigned int uIdx, unsigned int uLen = -1) const; void SetParams(const VCString& vsParams); + void SetParams(VCString&& vsParams); + + /// @deprecated use GetParamsColon() instead. + CString GetParams(unsigned int uIdx, unsigned int uLen = -1) const + ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") { + return GetParamsColon(uIdx, uLen); + } + CString GetParamsColon(unsigned int uIdx, unsigned int uLen = -1) const; CString GetParam(unsigned int uIdx) const; void SetParam(unsigned int uIdx, const CString& sParam); @@ -124,6 +140,7 @@ class CMessage { void SetTime(const timeval& ts) { m_time = ts; } const MCString& GetTags() const { return m_mssTags; } + MCString& GetTags() { return m_mssTags; } void SetTags(const MCString& mssTags) { m_mssTags = mssTags; } CString GetTag(const CString& sKey) const; @@ -136,12 +153,12 @@ class CMessage { }; CString ToString(unsigned int uFlags = IncludeAll) const; - void Parse(CString sMessage); + void Parse(const CString& sMessage); // Implicit and explicit conversion to a subclass reference. #ifndef SWIG template - M& As() ZNC_LVREFQUAL { + M& As() & { static_assert(std::is_base_of{}, "Must be subclass of CMessage"); static_assert(sizeof(M) == sizeof(CMessage), @@ -150,7 +167,7 @@ class CMessage { } template - const M& As() const ZNC_LVREFQUAL { + const M& As() const& { static_assert(std::is_base_of{}, "Must be subclass of CMessage"); static_assert(sizeof(M) == sizeof(CMessage), @@ -160,12 +177,12 @@ class CMessage { template {}>::type> - operator M&() ZNC_LVREFQUAL { + operator M&() & { return As(); } template {}>::type> - operator const M&() const ZNC_LVREFQUAL { + operator const M&() const& { return As(); } // REGISTER_ZNC_MESSAGE allows SWIG to instantiate correct .As<> calls. @@ -225,6 +242,13 @@ class CActionMessage : public CTargetMessage { }; REGISTER_ZNC_MESSAGE(CActionMessage); +class CAuthenticateMessage : public CMessage { + public: + CString GetText() const { return GetParam(0); } + void SetText(const CString& sText) { SetParam(0, sText); } +}; +REGISTER_ZNC_MESSAGE(CAuthenticateMessage); + class CCTCPMessage : public CTargetMessage { public: bool IsReply() const { return GetCommand().Equals("NOTICE"); } @@ -244,7 +268,14 @@ REGISTER_ZNC_MESSAGE(CJoinMessage); class CModeMessage : public CTargetMessage { public: - CString GetModes() const { return GetParams(1).TrimPrefix_n(":"); } + /// @deprecated Use GetModeList() and GetModeParams() + CString GetModes() const { return GetParamsColon(1).TrimPrefix_n(":"); } + + CString GetModeList() const { return GetParam(1); }; + + VCString GetModeParams() const { return GetParamsSplit(2); }; + + bool HasModes() const { return !GetModeList().empty(); }; }; REGISTER_ZNC_MESSAGE(CModeMessage); @@ -275,6 +306,8 @@ class CKickMessage : public CTargetMessage { void SetKickedNick(const CString& sNick) { SetParam(1, sNick); } CString GetReason() const { return GetParam(2); } void SetReason(const CString& sReason) { SetParam(2, sReason); } + CString GetText() const { return GetReason(); } + void SetText(const CString& sText) { SetReason(sText); } }; REGISTER_ZNC_MESSAGE(CKickMessage); @@ -282,6 +315,8 @@ class CPartMessage : public CTargetMessage { public: CString GetReason() const { return GetParam(1); } void SetReason(const CString& sReason) { SetParam(1, sReason); } + CString GetText() const { return GetReason(); } + void SetText(const CString& sText) { SetReason(sText); } }; REGISTER_ZNC_MESSAGE(CPartMessage); @@ -289,6 +324,8 @@ class CQuitMessage : public CMessage { public: CString GetReason() const { return GetParam(0); } void SetReason(const CString& sReason) { SetParam(0, sReason); } + CString GetText() const { return GetReason(); } + void SetText(const CString& sText) { SetReason(sText); } }; REGISTER_ZNC_MESSAGE(CQuitMessage); @@ -303,7 +340,18 @@ class CTopicMessage : public CTargetMessage { public: CString GetTopic() const { return GetParam(1); } void SetTopic(const CString& sTopic) { SetParam(1, sTopic); } + CString GetText() const { return GetTopic(); } + void SetText(const CString& sText) { SetTopic(sText); } }; REGISTER_ZNC_MESSAGE(CTopicMessage); +class CChgHostMessage : public CMessage { + public: + CString GetNewIdent() const { return GetParam(0); } + void SetNewIdent(const CString& sIdent) { SetParam(0, sIdent); } + CString GetNewHost() const { return GetParam(1); } + void SetNewHost(const CString& sHost) { SetParam(1, sHost); } +}; +REGISTER_ZNC_MESSAGE(CChgHostMessage); + #endif // !ZNC_MESSAGE_H diff --git a/include/znc/Modules.h b/include/znc/Modules.h index 2dfc5c57..7a6f6769 100644 --- a/include/znc/Modules.h +++ b/include/znc/Modules.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -166,6 +167,19 @@ class CFPTimer; class CSockManager; // !Forward Declarations +class CCapability { + public: + virtual ~CCapability() = default; + virtual void OnServerChangedSupport(CIRCNetwork* pNetwork, bool bState) {} + virtual void OnClientChangedSupport(CClient* pClient, bool bState) {} + + CModule* GetModule() { return m_pModule; } + void SetModule(CModule* p) { m_pModule = p; } + + protected: + CModule* m_pModule = nullptr; +}; + class CTimer : public CCron { public: CTimer(CModule* pModule, unsigned int uInterval, unsigned int uCycles, @@ -335,7 +349,7 @@ CModule* TModLoad(ModHandle p, CUser* pUser, CIRCNetwork* pNetwork, } /** A helper class for handling commands in modules. */ -class CModCommand { +class CModCommand : private CCoreTranslationMixin { public: /// Type for the callback function that handles the actual command. typedef void (CModule::*ModCmdFunc)(const CString& sLine); @@ -575,16 +589,21 @@ class CModule { * @param pOpNick The nick who sent the mode change, or nullptr if set by server. * @param Nick The nick whose channel mode changes. * @param Channel The channel on which the user mode is changed. - * @param uMode The mode character that is changed, e.g. '@' for op. + * @param cMode The mode character that is changed, e.g. '@' for op. * @param bAdded True if the mode is added, else false. * @param bNoChange true if this mode change doesn't change anything * because the nick already had this permission. * @see CIRCSock::GetModeType() for converting uMode into a mode (e.g. * 'o' for op). */ + virtual void OnChanPermission3(const CNick* pOpNick, const CNick& Nick, + CChan& Channel, char cMode, + bool bAdded, bool bNoChange); + /// @deprecated. Use OnChanPermission3. virtual void OnChanPermission2(const CNick* pOpNick, const CNick& Nick, CChan& Channel, unsigned char uMode, bool bAdded, bool bNoChange); + /// @deprecated. Use OnChanPermission3. virtual void OnChanPermission(const CNick& OpNick, const CNick& Nick, CChan& Channel, unsigned char uMode, bool bAdded, bool bNoChange); @@ -800,7 +819,6 @@ class CModule { /** This module hook is called when a client sends a raw traffic line to ZNC. * @param sLine The raw traffic line sent. - * @note The line does not include message tags. Use OnUserRawMessage() to access them. * @return See CModule::EModRet. */ virtual EModRet OnUserRaw(CString& sLine); @@ -994,13 +1012,28 @@ class CModule { virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic); /** Called for every CAP received via CAP LS from server. + * If you need to also advertise the cap to clients, use + * AddServerDependentCapability() instead. * @param sCap capability supported by server. * @return true if your module supports this CAP and * needs to turn it on with CAP REQ. */ virtual bool OnServerCapAvailable(const CString& sCap); + /** Called for every CAP received via CAP LS from server. + * By default just calls OnServerCapAvailable() without sValue, so + * overriding one of the two is enough. + * If you need to also advertise the cap to clients, use + * AddServerDependentCapability() instead. + * @param sCap capability name supported by server. + * @param sValue value. + * @return true if your module supports this CAP and + * needs to turn it on with CAP REQ. + */ + virtual bool OnServerCap302Available(const CString& sCap, const CString& sValue); /** Called for every CAP accepted or rejected by server * (with CAP ACK or CAP NAK after our CAP REQ). + * If you need to also advertise the cap to clients, use + * AddServerDependentCapability() instead. * @param sCap capability accepted/rejected by server. * @param bSuccess true if capability was accepted, false if rejected. */ @@ -1046,14 +1079,39 @@ class CModule { /// @deprecated Use OnSendToIRCMessage() instead. virtual EModRet OnSendToIRC(CString& sLine); + /** This module hook is called when a user sends a TAGMSG message. + * @since 1.10.0 + * @param Message The message which was sent. + * @return See CModule::EModRet. + */ + virtual EModRet OnUserTagMessage(CTargetMessage& Message); + /** Called when we receive a channel TAGMSG message from IRC. + * @since 1.10.0 + * @param Message The channel message. + * @return See CModule::EModRet. + */ + virtual EModRet OnChanTagMessage(CTargetMessage& Message); + /** Called when we receive a private TAGMSG message from IRC. + * @since 1.10.0 + * @param Message The message. + * @return See CModule::EModRet. + */ + virtual EModRet OnPrivTagMessage(CTargetMessage& Message); + ModHandle GetDLL() { return m_pDLL; } - /** This function sends a given raw IRC line to the IRC server, if we + /** This function sends a given IRC line to the IRC server, if we * are connected to one. Else this line is discarded. * @param sLine The line which should be sent. * @return true if the line was queued for sending. */ virtual bool PutIRC(const CString& sLine); + /** This function sends a given IRC message to the IRC server, if we + * are connected to one. Else this message is discarded. + * @param Message The message which should be sent. + * @return true if the message was queued for sending. + */ + virtual bool PutIRC(const CMessage& Message); /** This function sends a given raw IRC line to a client. * If we are in a module hook which is called for a specific client, * only that client will get the line, else all connected clients will @@ -1273,8 +1331,32 @@ class CModule { virtual EModRet OnUnknownUserRaw(CClient* pClient, CString& sLine); virtual EModRet OnUnknownUserRawMessage(CMessage& Message); + /** Called after login, and also during JumpNetwork. */ + virtual void OnClientAttached(); + /** Called upon disconnect, and also during JumpNetwork. */ + virtual void OnClientDetached(); + +#ifndef SWIG + /** Simple API to support client capabilities which depend on server to support that capability. + * It is built on top of other CAP related API, but removes boilerplate, + * and handles some tricky cases related to cap-notify and JumpNetwork. To + * use, create a subclass of CCapability, and pass to this function; it + * will automatically set the module pointer, then call the callbacks to + * notify you when server and client accepted support of the capability, or + * stopped supporting it. Note that it's not a strict toggle: e.g. + * sometimes client will disable the cap even when it was already disabled + * for that client. + * For perl and python modules, this function accepts 3 parameters: + * name, server callback, client callback; signatures of the callbacks are + * the same as of the virtual functions you'd implement in C++. + */ + void AddServerDependentCapability(const CString& sName, std::unique_ptr pCap); +#endif + /** Called when a client told us CAP LS. Use ssCaps.insert("cap-name") * for announcing capabilities which your module supports. + * If you need to adverite the cap to clients only when it's also supported + * by the server, use AddServerDependentCapability() instead. * @param pClient The client which requested the list. * @param ssCaps set of caps which will be sent to client. */ @@ -1288,6 +1370,8 @@ class CModule { virtual bool IsClientCapSupported(CClient* pClient, const CString& sCap, bool bState); /** Called when we actually need to turn a capability on or off for a client. + * If you need to adverite the cap to clients only when it's also supported + * by the server, use AddServerDependentCapability() instead. * If implementing a custom capability, make sure to call * pClient->SetTagSupport("tag-name", bState) for each tag that the * capability provides. @@ -1299,6 +1383,47 @@ class CModule { virtual void OnClientCapRequest(CClient* pClient, const CString& sCap, bool bState); + /** Called when a client requests SASL authentication. Use ssMechanisms.insert("MECHANISM") + * for announcing SASL mechanisms which your module supports. + * @param ssMechanisms The set of supported SASL mechanisms to append to. + * @since 1.10.0 + */ + virtual void OnClientGetSASLMechanisms(SCString& ssMechanisms); + /** Called when a client has selected a SASL mechanism for SASL authentication. + * If implementing a SASL authentication mechanism, set sResponse to + * specify an initial challenge message to send to the client. Otherwise, an + * empty response will be sent. To avoid sending any immediate response, + * return HALT; in that case the module should schedule calling + * GetClient()->SendSASLChallenge() with the initial response: in IRC SASL, + * server always responds first. + * @param sMechanism The SASL mechanism selected by the client. + * @param sResponse The optional value of an initial SASL challenge message + * to send to the client. + * @since 1.10.0 + */ + virtual EModRet OnClientSASLServerInitialChallenge( + const CString& sMechanism, CString& sResponse); + /** Called when a client is sending us a SASL message after the mechanism was selected. + * If implementing a SASL authentication mechanism, check the passed + * credentials, then either request more data by sending a challenge in + * GetClient()->SendSASLChallenge(), or reject authentication by calling + * GetClient()->RefuseSASLLogin(), or accept it by calling + * GetClient()->AcceptSASLLogin(). + * At some point before accepting the login, you should call + * GetClient()->ParseUser(authz-id) to let it know the network name to + * attach to and the client id. + * @param sMechanism The SASL mechanism selected by the client. + * @param sMessage The SASL opaque value/credentials sent by the client, + * after debase64ing and concatenating if it was split. + * @since 1.10.0 + */ + virtual EModRet OnClientSASLAuthenticate(const CString& sMechanism, + const CString& sMessage); + /** Called when a client sent '*' to abort SASL, or aborted it for another reason. + * @since 1.10.0 + */ + virtual void OnClientSASLAborted(); + /** Called when a module is going to be loaded. * @param sModName name of the module. * @param eType wanted type of the module (user/global). @@ -1349,6 +1474,26 @@ class CModule { const CString& sContext = "") const; #endif + // Default implementations of several callbacks to make + // AddServerDependentCapability work in modpython/modperl. + // Don't worry about existence of these functions. + bool InternalServerDependentCapsOnServerCap302Available( + const CString& sCap, const CString& sValue); + void InternalServerDependentCapsOnServerCapResult(const CString& sCap, + bool bSuccess); + void InternalServerDependentCapsOnClientCapLs(CClient* pClient, + SCString& ssCaps); + bool InternalServerDependentCapsIsClientCapSupported(CClient* pClient, + const CString& sCap, + bool bState); + void InternalServerDependentCapsOnClientCapRequest(CClient* pClient, + const CString& sCap, + bool bState); + void InternalServerDependentCapsOnClientAttached(); + void InternalServerDependentCapsOnClientDetached(); + void InternalServerDependentCapsOnIRCConnected(); + void InternalServerDependentCapsOnIRCDisconnected(); + protected: CModInfo::EModuleType m_eType; CString m_sDescription; @@ -1368,6 +1513,7 @@ class CModule { CString m_sArgs; CString m_sModPath; CTranslationDomainRefHolder m_Translation; + std::map> m_mServerDependentCaps; private: MCString @@ -1376,7 +1522,7 @@ class CModule { std::map m_mCommands; }; -class CModules : public std::vector { +class CModules : public std::vector, private CCoreTranslationMixin { public: CModules(); ~CModules(); @@ -1404,6 +1550,9 @@ class CModules : public std::vector { CString& sRealName); bool OnBroadcast(CString& sMessage); + bool OnChanPermission3(const CNick* pOpNick, const CNick& Nick, + CChan& Channel, char cMode, bool bAdded, + bool bNoChange); bool OnChanPermission2(const CNick* pOpNick, const CNick& Nick, CChan& Channel, unsigned char uMode, bool bAdded, bool bNoChange); @@ -1498,6 +1647,9 @@ class CModules : public std::vector { bool OnUserTopicRequest(CString& sChannel); bool OnUserQuit(CString& sMessage); bool OnUserQuitMessage(CQuitMessage& Message); + bool OnUserTagMessage(CTargetMessage& Message); + bool OnChanTagMessage(CTargetMessage& Message); + bool OnPrivTagMessage(CTargetMessage& Message); bool OnCTCPReply(CNick& Nick, CString& sMessage); bool OnCTCPReplyMessage(CCTCPMessage& Message); @@ -1528,8 +1680,10 @@ class CModules : public std::vector { bool OnSendToClientMessage(CMessage& Message); bool OnSendToIRC(CString& sLine); bool OnSendToIRCMessage(CMessage& Message); + bool OnClientAttached(); + bool OnClientDetached(); - bool OnServerCapAvailable(const CString& sCap); + bool OnServerCapAvailable(const CString& sCap, const CString& sValue); bool OnServerCapResult(const CString& sCap, bool bSuccess); CModule* FindModule(const CString& sModule) const; @@ -1574,6 +1728,14 @@ class CModules : public std::vector { bool IsClientCapSupported(CClient* pClient, const CString& sCap, bool bState); bool OnClientCapRequest(CClient* pClient, const CString& sCap, bool bState); + + bool OnClientGetSASLMechanisms(SCString& ssMechanisms); + bool OnClientSASLAborted(); + bool OnClientSASLServerInitialChallenge(const CString& sMechanism, + CString& sResponse); + bool OnClientSASLAuthenticate(const CString& sMechanism, + const CString& sBuffer); + bool OnModuleLoading(const CString& sModName, const CString& sArgs, CModInfo::EModuleType eType, bool& bSuccess, CString& sRetMsg); @@ -1587,6 +1749,7 @@ class CModules : public std::vector { private: static ModHandle OpenModule(const CString& sModule, const CString& sModPath, CModInfo& Info, CString& sRetMsg); + static bool ValidateModuleName(const CString& sModule, CString& sRetMsg); protected: CUser* m_pUser; diff --git a/include/znc/Nick.h b/include/znc/Nick.h index 3716e1e3..6bd8aa64 100644 --- a/include/znc/Nick.h +++ b/include/znc/Nick.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,14 +47,19 @@ class CNick { void SetNick(const CString& s); void SetIdent(const CString& s); void SetHost(const CString& s); - bool AddPerm(unsigned char uPerm); - bool RemPerm(unsigned char uPerm); + /// e.g. '@' for chanop. + bool AddPerm(char cPerm); + /// e.g. '@' for chanop. + bool RemPerm(char cPerm); // !Setters // Getters + /// e.g. '@' for chanop. CString GetPermStr() const; - unsigned char GetPermChar() const; - bool HasPerm(unsigned char uPerm) const; + /// e.g. '@' for chanop. + char GetPermChar() const; + /// e.g. '@' for chanop. + bool HasPerm(char cPerm) const; const CString& GetNick() const; const CString& GetIdent() const; const CString& GetHost() const; diff --git a/include/znc/Query.h b/include/znc/Query.h index a1a22bb4..7baaed21 100644 --- a/include/znc/Query.h +++ b/include/znc/Query.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/SSLVerifyHost.h b/include/znc/SSLVerifyHost.h index 1fbb9403..9f5ffb77 100644 --- a/include/znc/SSLVerifyHost.h +++ b/include/znc/SSLVerifyHost.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/Server.h b/include/znc/Server.h index 43dceaff..4598666f 100644 --- a/include/znc/Server.h +++ b/include/znc/Server.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/Socket.h b/include/znc/Socket.h index dda2435f..249304b5 100644 --- a/include/znc/Socket.h +++ b/include/znc/Socket.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ class CModule; -class CZNCSock : public Csock, public CCoreTranslationMixin { +class CZNCSock : public Csock, protected CCoreTranslationMixin { public: CZNCSock(int timeout = 60); CZNCSock(const CString& sHost, u_short port, int timeout = 60); @@ -36,12 +36,16 @@ class CZNCSock : public Csock, public CCoreTranslationMixin { int VerifyPeerCertificate(int iPreVerify, X509_STORE_CTX* pStoreCTX) override; void SSLHandShakeFinished() override; + bool CheckSSLCert(X509* pCert); + virtual void SSLCertError(X509* pCert) {} bool SNIConfigureClient(CString& sHostname) override; + CString GetSSLPeerFingerprint(X509* pCert = nullptr) const; +#else + CString GetSSLPeerFingerprint() const { return ""; } #endif void SetHostToVerifySSL(const CString& sHost) { m_sHostToVerifySSL = sHost; } - CString GetSSLPeerFingerprint() const; void SetSSLTrustedPeerFingerprints(const SCString& ssFPs) { m_ssTrustedFingerprints = ssFPs; } @@ -72,7 +76,8 @@ class CZNCSock : public Csock, public CCoreTranslationMixin { enum EAddrType { ADDR_IPV4ONLY, ADDR_IPV6ONLY, ADDR_ALL }; -class CSockManager : public TSocketManager { +class CSockManager : public TSocketManager, + private CCoreTranslationMixin { public: CSockManager(); virtual ~CSockManager(); diff --git a/include/znc/Template.h b/include/znc/Template.h index 16837eb9..7ec8e53d 100644 --- a/include/znc/Template.h +++ b/include/znc/Template.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/Threads.h b/include/znc/Threads.h index 8f741c56..343360b9 100644 --- a/include/znc/Threads.h +++ b/include/znc/Threads.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/Translation.h b/include/znc/Translation.h index 8ecfd2c2..76b8b4a2 100644 --- a/include/znc/Translation.h +++ b/include/znc/Translation.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ #include #include +#include struct CTranslationInfo { static std::map GetTranslations(); @@ -83,20 +84,18 @@ class CDelayedTranslation { class COptionalTranslation { public: - COptionalTranslation(const CString& sText) - : m_bTranslating(false), m_sText(sText) {} + COptionalTranslation(const CString& sText) : m_text(sText) {} COptionalTranslation(const char* s) : COptionalTranslation(CString(s)) {} - COptionalTranslation(const CDelayedTranslation& dTranslation) - : m_bTranslating(true), m_dTranslation(dTranslation) {} + COptionalTranslation(const CDelayedTranslation& dTranslation) : m_text(dTranslation) {} CString Resolve() const { - return m_bTranslating ? m_dTranslation.Resolve() : m_sText; + if (m_text.index() == 0) { + return std::get<0>(m_text); + } + return std::get<1>(m_text).Resolve(); } private: - bool m_bTranslating; - // TODO switch to std::variant after C++17 - CString m_sText; - CDelayedTranslation m_dTranslation; + std::variant m_text; }; // Used by everything except modules. diff --git a/include/znc/User.h b/include/znc/User.h index d9bcca6e..d80a10b4 100644 --- a/include/znc/User.h +++ b/include/znc/User.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -34,9 +35,9 @@ class CIRCSock; class CUserTimer; class CServer; -class CUser { +class CUser : private CCoreTranslationMixin { public: - CUser(const CString& sUserName); + CUser(const CString& sUsername); ~CUser(); CUser(const CUser&) = delete; @@ -44,31 +45,38 @@ class CUser { bool ParseConfig(CConfig* Config, CString& sError); - // TODO refactor this enum eHashType { HASH_NONE, HASH_MD5, HASH_SHA256, + HASH_ARGON2ID, - HASH_DEFAULT = HASH_SHA256 + // This should be kept in sync with CUtils::SaltedHash +#if ZNC_HAVE_ARGON + HASH_DEFAULT = HASH_ARGON2ID, +#else + HASH_DEFAULT = HASH_SHA256, +#endif }; - // If you change the default hash here and in HASH_DEFAULT, - // don't forget CUtils::sDefaultHash! - // TODO refactor this static CString SaltedHash(const CString& sPass, const CString& sSalt) { - return CUtils::SaltedSHA256Hash(sPass, sSalt); + return CUtils::SaltedHash(sPass, sSalt); } CConfig ToConfig() const; - bool CheckPass(const CString& sPass) const; + /** Checks password, may upgrade the hash method. */ + bool CheckPass(const CString& sPass); bool AddAllowedHost(const CString& sHostMask); bool RemAllowedHost(const CString& sHostMask); void ClearAllowedHosts(); bool IsHostAllowed(const CString& sHost) const; bool IsValid(CString& sErrMsg, bool bSkipPass = false) const; - static bool IsValidUserName(const CString& sUserName); - static CString MakeCleanUserName(const CString& sUserName); + static bool IsValidUsername(const CString& sUsername); + /** @deprecated Use IsValidUsername() instead. */ + static bool IsValidUserName(const CString& sUsername); + static CString MakeCleanUsername(const CString& sUsername); + /** @deprecated Use MakeCleanUsername() instead. */ + static CString MakeCleanUserName(const CString& sUsername); // Modules CModules& GetModules() { return *m_pModules; } @@ -88,7 +96,10 @@ class CUser { bool PutUser(const CString& sLine, CClient* pClient = nullptr, CClient* pSkipClient = nullptr); bool PutAllUser(const CString& sLine, CClient* pClient = nullptr, - CClient* pSkipClient = nullptr); + CClient* pSkipClient = nullptr) + ZNC_MSG_DEPRECATED("Use PutUser() instead") { + return PutUser(sLine, pClient, pSkipClient); + } bool PutStatus(const CString& sLine, CClient* pClient = nullptr, CClient* pSkipClient = nullptr); bool PutStatusNotice(const CString& sLine, CClient* pClient = nullptr, @@ -110,6 +121,7 @@ class CUser { CString AddTimestamp(const CString& sStr) const; CString AddTimestamp(time_t tm, const CString& sStr) const; + CString AddTimestamp(timeval tv, const CString& sStr) const; void CloneNetworks(const CUser& User); bool Clone(const CUser& User, CString& sErrorRet, @@ -131,6 +143,11 @@ class CUser { void SetDenyLoadMod(bool b); void SetAdmin(bool b); void SetDenySetBindHost(bool b); + void SetDenySetIdent(bool b); + void SetDenySetNetwork(bool b); + void SetDenySetRealName(bool b); + void SetDenySetQuitMsg(bool b); + void SetDenySetCTCPReplies(bool b); bool SetStatusPrefix(const CString& s); void SetDefaultChanModes(const CString& s); void SetClientEncoding(const CString& s); @@ -149,6 +166,7 @@ class CUser { void SetTimestampFormat(const CString& s) { m_sTimestampFormat = s; } void SetTimestampAppend(bool b) { m_bAppendTimestamp = b; } void SetTimestampPrepend(bool b) { m_bPrependTimestamp = b; } + void SetAuthOnlyViaModule(bool b) { m_bAuthOnlyViaModule = b; } void SetTimezone(const CString& s) { m_sTimezone = s; } void SetJoinTries(unsigned int i) { m_uMaxJoinTries = i; } void SetMaxJoins(unsigned int i) { m_uMaxJoins = i; } @@ -161,7 +179,9 @@ class CUser { // Getters const std::vector& GetUserClients() const { return m_vClients; } std::vector GetAllClients() const; + /** @deprecated Use GetUsername() instead. */ const CString& GetUserName() const; + const CString& GetUsername() const; const CString& GetCleanUserName() const; const CString& GetNick(bool bAllowDefault = true) const; const CString& GetAltNick(bool bAllowDefault = true) const; @@ -183,7 +203,13 @@ class CUser { bool DenyLoadMod() const; bool IsAdmin() const; bool DenySetBindHost() const; + bool DenySetIdent() const; + bool DenySetNetwork() const; + bool DenySetRealName() const; + bool DenySetQuitMsg() const; + bool DenySetCTCPReplies() const; bool MultiClients() const; + bool AuthOnlyViaModule() const; const CString& GetStatusPrefix() const; const CString& GetDefaultChanModes() const; /** How long must an IRC connection be idle before ZNC sends a ping */ @@ -216,8 +242,8 @@ class CUser { // !Getters protected: - const CString m_sUserName; - const CString m_sCleanUserName; + const CString m_sUsername; + const CString m_sCleanUsername; CString m_sNick; CString m_sAltNick; CString m_sIdent; @@ -244,11 +270,17 @@ class CUser { bool m_bDenyLoadMod; bool m_bAdmin; bool m_bDenySetBindHost; + bool m_bDenySetIdent; + bool m_bDenySetNetwork; + bool m_bDenySetRealName; + bool m_bDenySetQuitMsg; + bool m_bDenySetCTCPReplies; bool m_bAutoClearChanBuffer; bool m_bAutoClearQueryBuffer; bool m_bBeingDeleted; bool m_bAppendTimestamp; bool m_bPrependTimestamp; + bool m_bAuthOnlyViaModule; CUserTimer* m_pUserTimer; diff --git a/include/znc/Utils.h b/include/znc/Utils.h index 5cc74d84..52fe6471 100644 --- a/include/znc/Utils.h +++ b/include/znc/Utils.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,7 @@ class CUtils { static CString GetIP(unsigned long addr); static unsigned long GetLongIP(const CString& sIP); + static CString GetHostName(); static void PrintError(const CString& sMessage); static void PrintMessage(const CString& sMessage, bool bStrong = false); @@ -51,15 +52,16 @@ class CUtils { static void PrintAction(const CString& sMessage); static void PrintStatus(bool bSuccess, const CString& sMessage = ""); -#ifndef SWIGPERL - // TODO refactor this - static const CString sDefaultHash; -#endif + /** Asks password from stdin, with confirmation. + * + * @returns Piece of znc.conf with block + * */ + static CString AskSaltedHashPassForConfig(); - static CString GetSaltedHashPass(CString& sSalt); static CString GetSalt(); static CString SaltedMD5Hash(const CString& sPass, const CString& sSalt); static CString SaltedSHA256Hash(const CString& sPass, const CString& sSalt); + static CString SaltedHash(const CString& sPass, const CString& sSalt); static CString GetPass(const CString& sPrompt); static bool GetInput(const CString& sPrompt, CString& sRet, const CString& sDefault = "", @@ -73,12 +75,27 @@ class CUtils { static timeval GetTime(); static unsigned long long GetMillTime(); #ifdef HAVE_LIBSSL - static void GenerateCert(FILE* pOut, const CString& sHost = ""); + static void GenerateCert(FILE* pOut); #endif /* HAVE_LIBSSL */ static CString CTime(time_t t, const CString& sTZ); static CString FormatTime(time_t t, const CString& sFormat, const CString& sTZ); + /** Supports an additional format specifier for formatting sub-second values: + * + * - %f - sub-second fraction + * - %3f - millisecond (default, if no width is specified) + * - %6f - microsecond + * + * However, note that timeval only supports microsecond precision + * (thus, formatting with higher-than-microsecond precision will + * always result in trailing zeroes), and IRC server-time is specified + * in millisecond precision (thus formatting received timestamps with + * higher-than-millisecond precision will always result in trailing + * zeroes). + */ + static CString FormatTime(const timeval& tv, const CString& sFormat, + const CString& sTZ); static CString FormatServerTime(const timeval& tv); static timeval ParseServerTime(const CString& sTime); static SCString GetTimezones(); @@ -113,7 +130,7 @@ class CException { }; -/** Generate a grid-like output from a given input. +/** Generate a grid-like or list-like output from a given input. * * @code * CTable table; @@ -137,9 +154,25 @@ class CException { +-------+-------+ | hello | world | +-------+-------+@endverbatim + * + * If the table has at most two columns, one can switch to ListStyle output + * like so: + * @code + * CTable table; + * table.AddColumn("a"); + * table.AddColumn("b"); + * table.SetStyle(CTable::ListStyle); + * // ... + * @endcode + * + * This will yield the following (Note that the header is omitted; asterisks + * denote bold text): + * @verbatim +*hello*: world@endverbatim */ class CTable : protected std::vector> { public: + enum EStyle { GridStyle, ListStyle }; CTable() {} virtual ~CTable() {} @@ -147,10 +180,18 @@ class CTable : protected std::vector> { * Please note that you should add all columns before starting to fill * the table! * @param sName The name of the column. - * @return false if a column by that name already existed. + * @return false if a column by that name already existed or the current + * style does not allow this many columns. */ bool AddColumn(const CString& sName); + /** Selects the output style of the table. + * Select between different styles for printing. Default is GridStyle. + * @param eNewStyle Table style type. + * @return false if the style cannot be applied (usually too many columns). + */ + bool SetStyle(EStyle eNewStyle); + /** Adds a new row to the table. * After calling this you can fill the row with content. * @return The index of this row @@ -198,6 +239,7 @@ class CTable : protected std::vector> { std::vector m_vsHeaders; // Used to cache the width of a column std::map m_msuWidths; + EStyle eStyle = GridStyle; }; #ifdef HAVE_LIBSSL diff --git a/include/znc/WebModules.h b/include/znc/WebModules.h index 2b2b7e69..e3519d64 100644 --- a/include/znc/WebModules.h +++ b/include/znc/WebModules.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/ZNCDebug.h b/include/znc/ZNCDebug.h index d81fe93c..2c7757da 100644 --- a/include/znc/ZNCDebug.h +++ b/include/znc/ZNCDebug.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/ZNCString.h b/include/znc/ZNCString.h index af2bd5c4..3eda9e51 100644 --- a/include/znc/ZNCString.h +++ b/include/znc/ZNCString.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -697,4 +697,11 @@ class CInlineFormatMessage { CString m_sFormat; }; +// For gtest +#ifdef GTEST_FAIL +inline void PrintTo(const CString& s, std::ostream* os) { + *os << '"' << s.Escape_n(CString::EDEBUG) << '"'; +} +#endif + #endif // !ZNCSTRING_H diff --git a/include/znc/defines.h b/include/znc/defines.h index 184b54dc..d4384b97 100644 --- a/include/znc/defines.h +++ b/include/znc/defines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/main.h b/include/znc/main.h index f3944ddf..7d22fd43 100644 --- a/include/znc/main.h +++ b/include/znc/main.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/include/znc/version.h b/include/znc/version.h index 8cadeb2e..be5e50d4 100644 --- a/include/znc/version.h +++ b/include/znc/version.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,19 +16,9 @@ limitations under the License. #ifndef ZNC_VERSION_H #define ZNC_VERSION_H -#ifndef BUILD_WITH_CMAKE -// The following defines are for #if comparison (preprocessor only likes ints) -#define VERSION_MAJOR 1 -#define VERSION_MINOR 7 -#define VERSION_PATCH -1 -// This one is for display purpose and to check ABI compatibility of modules -#define VERSION_STR "1.7.x" -#endif - // Don't use this one #define VERSION (VERSION_MAJOR + VERSION_MINOR / 10.0) -// autoconf: You can add -DVERSION_EXTRA="stuff" to your CXXFLAGS! // CMake: You can add -DVERSION_EXTRA=stuff to cmake! #ifndef VERSION_EXTRA #define VERSION_EXTRA "" @@ -67,9 +57,16 @@ extern const char* ZNC_VERSION_EXTRA; #define ZNC_VERSION_TEXT_I18N "no" #endif +// This is only here because HASH_DEFAULT has different value +#ifdef ZNC_HAVE_ARGON +#define ZNC_VERSION_TEXT_ARGON "yes" +#else +#define ZNC_VERSION_TEXT_ARGON "no" +#endif + #define ZNC_COMPILE_OPTIONS_STRING \ "IPv6: " ZNC_VERSION_TEXT_IPV6 ", SSL: " ZNC_VERSION_TEXT_SSL \ ", DNS: " ZNC_VERSION_TEXT_DNS ", charset: " ZNC_VERSION_TEXT_ICU \ - ", i18n: " ZNC_VERSION_TEXT_I18N + ", i18n: " ZNC_VERSION_TEXT_I18N ", Argon2: " ZNC_VERSION_TEXT_ARGON #endif // !ZNC_VERSION_H diff --git a/include/znc/znc.h b/include/znc/znc.h index 85b07125..ef3eb2dd 100644 --- a/include/znc/znc.h +++ b/include/znc/znc.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ class CConfigWriteTimer; class CConfig; class CFile; -class CZNC { +class CZNC : private CCoreTranslationMixin { public: CZNC(); ~CZNC(); @@ -123,6 +123,7 @@ class CZNC { } void SetProtectWebSessions(bool b) { m_bProtectWebSessions = b; } void SetHideVersion(bool b) { m_bHideVersion = b; } + void SetAuthOnlyViaModule(bool b) { m_bAuthOnlyViaModule = b; } void SetConnectDelay(unsigned int i); void SetSSLCiphers(const CString& sCiphers) { m_sSSLCiphers = sCiphers; } bool SetSSLProtocols(const CString& sProtocols); @@ -166,6 +167,7 @@ class CZNC { unsigned int GetConnectDelay() const { return m_uiConnectDelay; } bool GetProtectWebSessions() const { return m_bProtectWebSessions; } bool GetHideVersion() const { return m_bHideVersion; } + bool GetAuthOnlyViaModule() const { return m_bAuthOnlyViaModule; } CString GetSSLCiphers() const { return m_sSSLCiphers; } CString GetSSLProtocols() const { return m_sSSLProtocols; } Csock::EDisableProtocol GetDisabledSSLProtocols() const { @@ -261,6 +263,8 @@ class CZNC { static void DumpConfig(const CConfig* Config); private: + static CString FormatBindError(); + CFile* InitPidFile(); bool ReadConfig(CConfig& config, CString& sError); @@ -315,6 +319,7 @@ class CZNC { TCacheMap m_sConnectThrottle; bool m_bProtectWebSessions; bool m_bHideVersion; + bool m_bAuthOnlyViaModule; CTranslationDomainRefHolder m_Translation; unsigned int m_uiConfigWriteDelay; CConfigWriteTimer* m_pConfigTimer; diff --git a/include/znc/zncconfig.h.cmake.in b/include/znc/zncconfig.h.cmake.in index f31f7129..230ecef1 100644 --- a/include/znc/zncconfig.h.cmake.in +++ b/include/znc/zncconfig.h.cmake.in @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,9 +31,11 @@ #define HAVE_PTHREAD 1 #cmakedefine HAVE_THREADED_DNS 1 #cmakedefine HAVE_LIBSSL 1 +#cmakedefine HAVE_SSL_SESSION_get0_cipher 1 #cmakedefine HAVE_IPV6 1 #cmakedefine HAVE_ZLIB 1 #cmakedefine HAVE_I18N 1 +#cmakedefine ZNC_HAVE_ARGON 1 #cmakedefine CSOCK_USE_POLL 1 #cmakedefine HAVE_GETOPT_LONG 1 @@ -41,6 +43,8 @@ #cmakedefine HAVE_TCSETATTR 1 #cmakedefine HAVE_GETPASSPHRASE 1 #cmakedefine HAVE_CLOCK_GETTIME 1 +#cmakedefine ZNC_HAVE_GETHOSTNAME 1 +#cmakedefine ZNC_HAVE_UNAME 1 #cmakedefine HAVE_ICU 1 #define U_USING_ICU_NAMESPACE 1 diff --git a/logo.png b/logo.png new file mode 100644 index 00000000..4cf0c6c0 Binary files /dev/null and b/logo.png differ diff --git a/m4/ac_pkg_swig.m4 b/m4/ac_pkg_swig.m4 deleted file mode 100644 index a9da4eda..00000000 --- a/m4/ac_pkg_swig.m4 +++ /dev/null @@ -1,210 +0,0 @@ -dnl @synopsis AC_PROG_SWIG([major.minor.micro]) -dnl -dnl NOTICE: for new code, use http://www.gnu.org/s/autoconf-archive/ax_pkg_swig.html instead. -dnl -dnl This macro searches for a SWIG installation on your system. If -dnl found you should call SWIG via $(SWIG). You can use the optional -dnl first argument to check if the version of the available SWIG is -dnl greater than or equal to the value of the argument. It should have -dnl the format: N[.N[.N]] (N is a number between 0 and 999. Only the -dnl first N is mandatory.) -dnl -dnl If the version argument is given (e.g. 1.3.17), AC_PROG_SWIG checks -dnl that the swig package is this version number or higher. -dnl -dnl In configure.in, use as: -dnl -dnl AC_PROG_SWIG(1.3.17) -dnl SWIG_ENABLE_CXX -dnl SWIG_MULTI_MODULE_SUPPORT -dnl SWIG_PYTHON -dnl -dnl @category InstalledPackages -dnl @author Sebastian Huber -dnl @author Alan W. Irwin -dnl @author Rafael Laboissiere -dnl @author Andrew Collier -dnl @version 2004-09-20 -dnl -dnl Modified by Alexey Sokolov on 2012-08-08 -dnl @license GPLWithACException -dnl -dnl NOTICE: for new code, use http://www.gnu.org/s/autoconf-archive/ax_pkg_swig.html instead. - -AC_DEFUN([AC_PROG_SWIG],[ - SWIG_ERROR="" - - if test -n "$1"; then - # Calculate the required version number components - [required=$1] - [required_major=`echo $required | sed 's/[^0-9].*//'`] - if test -z "$required_major" ; then - [required_major=0] - fi - [required=`echo $required | sed 's/[0-9]*[^0-9]//'`] - [required_minor=`echo $required | sed 's/[^0-9].*//'`] - if test -z "$required_minor" ; then - [required_minor=0] - fi - [required=`echo $required | sed 's/[0-9]*[^0-9]//'`] - [required_patch=`echo $required | sed 's/[^0-9].*//'`] - if test -z "$required_patch" ; then - [required_patch=0] - fi - fi - - # for "python 3 abc set" and "PyInt_FromSize_t in python3" checks - - cat <<-END > conftest-python.i - %module conftest; - %include - %include - %template(SInt) std::set; - END - - # check if perl has std::...::size_type defined. Don't add new tests to this .i; it'll break this test due to check for "NewPointerObj((" - - cat <<-END > conftest-perl.i - %module conftest; - %include - %include - %include - %template() std::vector; - %template() std::list; - %template() std::deque; - std::vector::size_type checkVector(); - std::list::size_type checkList(); - std::deque::size_type checkDeque(); - END - SWIG_installed_versions="" - AC_CACHE_CHECK([for SWIG >= $1], [znc_cv_path_SWIG], [ - AC_PATH_PROGS_FEATURE_CHECK([SWIG], [swig swig2.0 swig3.0], [ - echo trying $ac_path_SWIG >&AS_MESSAGE_LOG_FD - $ac_path_SWIG -version >&AS_MESSAGE_LOG_FD - [swig_version=`$ac_path_SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`] - if test -n "$swig_version"; then - swig_right_version=1 - - SWIG_installed_versions="$SWIG_installed_versions $swig_version " - - if test -n "$required"; then - # Calculate the available version number components - [available=$swig_version] - - [available_major=`echo $available | sed 's/[^0-9].*//'`] - if test -z "$available_major" ; then - [available_major=0] - fi - [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] - [available_minor=`echo $available | sed 's/[^0-9].*//'`] - if test -z "$available_minor" ; then - [available_minor=0] - fi - [available=`echo $available | sed 's/[0-9]*[^0-9]//'`] - [available_patch=`echo $available | sed 's/[^0-9].*//'`] - if test -z "$available_patch" ; then - [available_patch=0] - fi - - if test $available_major -lt $required_major; then - swig_right_version=0 - elif test $available_major -eq $required_major; then - if test $available_minor -lt $required_minor; then - swig_right_version=0 - elif test $available_minor -eq $required_minor; then - if test $available_patch -lt $required_patch; then - swig_right_version=0 - fi - fi - fi - fi - - if test $swig_right_version -eq 1; then - # "python 3 abc set", "PyInt_FromSize_t in python3" and "perl size_type" checks - echo "checking behavior of this SWIG" >&AS_MESSAGE_LOG_FD - - $ac_path_SWIG -python -py3 -c++ -shadow conftest-python.i >&AS_MESSAGE_LOG_FD && \ - echo "python wrapper created" >&AS_MESSAGE_LOG_FD && \ - echo "testing std::set... ">&AS_MESSAGE_LOG_FD && \ - grep SInt_discard conftest.py > /dev/null 2>&1 && \ - echo "std::set works" >&AS_MESSAGE_LOG_FD && \ - echo "testing PyInt_FromSize_t..." >&AS_MESSAGE_LOG_FD && \ - grep '#define PyInt_FromSize_t' conftest-python_wrap.cxx > /dev/null 2>&1 && \ - echo "PyInt_FromSize_t is defined" >&AS_MESSAGE_LOG_FD && \ - $ac_path_SWIG -perl -c++ -shadow conftest-perl.i >&AS_MESSAGE_LOG_FD && \ - echo "perl wrapper created" >&AS_MESSAGE_LOG_FD && \ - echo "testing size_type..." >&AS_MESSAGE_LOG_FD && \ - test 0 -eq `grep -c 'NewPointerObj((' conftest-perl_wrap.cxx` && \ - echo "size_type work" >&AS_MESSAGE_LOG_FD && \ - znc_cv_path_SWIG=$ac_path_SWIG \ - ac_path_SWIG_found=: - if test "x$ac_path_SWIG_found" != "x:"; then - echo "fail" >&AS_MESSAGE_LOG_FD - fi - rm -f conftest-python_wrap.cxx conftest.py - rm -f conftest-perl_wrap.cxx conftest.pm - - - else - echo "SWIG version >= $1 is required. You have '$swig_version'" >&AS_MESSAGE_LOG_FD - fi - fi - echo end trying $ac_path_SWIG >&AS_MESSAGE_LOG_FD - ]) - ]) - rm -f conftest-python.i conftest-perl.i - if test -n "$SWIG_installed_versions"; then - AC_MSG_NOTICE([Following SWIG versions are found:$SWIG_installed_versions]) - fi - - AC_SUBST([SWIG], [$znc_cv_path_SWIG]) - if test -n "$SWIG"; then - SWIG_LIB=`$SWIG -swiglib` - fi - AC_SUBST([SWIG_LIB]) -]) - -# SWIG_ENABLE_CXX() -# -# Enable SWIG C++ support. This affects all invocations of $(SWIG). -AC_DEFUN([SWIG_ENABLE_CXX],[ - AC_REQUIRE([AC_PROG_SWIG]) - AC_REQUIRE([AC_PROG_CXX]) - SWIG="$SWIG -c++" -]) - -# SWIG_MULTI_MODULE_SUPPORT() -# -# Enable support for multiple modules. This effects all invocations -# of $(SWIG). You have to link all generated modules against the -# appropriate SWIG runtime library. If you want to build Python -# modules for example, use the SWIG_PYTHON() macro and link the -# modules against $(SWIG_PYTHON_LIBS). -# -AC_DEFUN([SWIG_MULTI_MODULE_SUPPORT],[ - AC_REQUIRE([AC_PROG_SWIG]) - SWIG="$SWIG -noruntime" -]) - -# SWIG_PYTHON([use-shadow-classes = {no, yes}]) -# -# Checks for Python and provides the $(SWIG_PYTHON_CPPFLAGS), -# and $(SWIG_PYTHON_OPT) output variables. -# -# $(SWIG_PYTHON_OPT) contains all necessary SWIG options to generate -# code for Python. Shadow classes are enabled unless the value of the -# optional first argument is exactly 'no'. If you need multi module -# support (provided by the SWIG_MULTI_MODULE_SUPPORT() macro) use -# $(SWIG_PYTHON_LIBS) to link against the appropriate library. It -# contains the SWIG Python runtime library that is needed by the type -# check system for example. -AC_DEFUN([SWIG_PYTHON],[ - AC_REQUIRE([AC_PROG_SWIG]) - AC_REQUIRE([AC_PYTHON_DEVEL]) - test "x$1" != "xno" || swig_shadow=" -noproxy" - AC_SUBST([SWIG_PYTHON_OPT],[-python$swig_shadow]) - AC_SUBST([SWIG_PYTHON_CPPFLAGS],[$PYTHON_CPPFLAGS]) -]) - - - diff --git a/m4/ax_cxx_compile_stdcxx_11.m4 b/m4/ax_cxx_compile_stdcxx_11.m4 deleted file mode 100644 index 20c8b120..00000000 --- a/m4/ax_cxx_compile_stdcxx_11.m4 +++ /dev/null @@ -1,163 +0,0 @@ -# ============================================================================ -# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html -# ============================================================================ -# -# SYNOPSIS -# -# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) -# -# DESCRIPTION -# -# Check for baseline language coverage in the compiler for the C++11 -# standard; if necessary, add switches to CXXFLAGS to enable support. -# -# The first argument, if specified, indicates whether you insist on an -# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. -# -std=c++11). If neither is specified, you get whatever works, with -# preference for an extended mode. -# -# The second argument, if specified 'mandatory' or if left unspecified, -# indicates that baseline C++11 support is required and that the macro -# should error out if no mode with that support is found. If specified -# 'optional', then configuration proceeds regardless, after defining -# HAVE_CXX11 if and only if a supporting mode is found. -# -# LICENSE -# -# Copyright (c) 2008 Benjamin Kosnik -# Copyright (c) 2012 Zack Weinberg -# Copyright (c) 2013 Roy Stogner -# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 5 - -m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[ - template - struct check - { - static_assert(sizeof(int) <= sizeof(T), "not big enough"); - }; - - struct Base { - virtual void f() {} - }; - struct Child : public Base { - virtual void f() override {} - }; - - typedef check> right_angle_brackets; - - int a; - decltype(a) b; - - typedef check check_type; - check_type c; - check_type&& cr = static_cast(c); - - auto d = a; - auto l = [](){}; - - // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae - // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this - namespace test_template_alias_sfinae { - struct foo {}; - - template - using member = typename T::member_type; - - template - void func(...) {} - - template - void func(member*) {} - - void test(); - - void test() { - func(0); - } - } -]]) - -AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl - m4_if([$1], [], [], - [$1], [ext], [], - [$1], [noext], [], - [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl - m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], - [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], - [$2], [optional], [ax_cxx_compile_cxx11_required=false], - [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])]) - AC_LANG_PUSH([C++])dnl - ac_success=no - AC_CACHE_CHECK(whether $CXX supports C++11 features by default, - ax_cv_cxx_compile_cxx11, - [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], - [ax_cv_cxx_compile_cxx11=yes], - [ax_cv_cxx_compile_cxx11=no])]) - if test x$ax_cv_cxx_compile_cxx11 = xyes; then - ac_success=yes - fi - - m4_if([$1], [noext], [], [dnl - if test x$ac_success = xno; then - for switch in -std=gnu++11 -std=gnu++0x; do - cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) - AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, - $cachevar, - [ac_save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $switch" - AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], - [eval $cachevar=yes], - [eval $cachevar=no]) - CXXFLAGS="$ac_save_CXXFLAGS"]) - if eval test x\$$cachevar = xyes; then - CXXFLAGS="$CXXFLAGS $switch" - ac_success=yes - break - fi - done - fi]) - - m4_if([$1], [ext], [], [dnl - if test x$ac_success = xno; then - for switch in -std=c++11 -std=c++0x; do - cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) - AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, - $cachevar, - [ac_save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $switch" - AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], - [eval $cachevar=yes], - [eval $cachevar=no]) - CXXFLAGS="$ac_save_CXXFLAGS"]) - if eval test x\$$cachevar = xyes; then - CXXFLAGS="$CXXFLAGS $switch" - ac_success=yes - break - fi - done - fi]) - AC_LANG_POP([C++]) - if test x$ax_cxx_compile_cxx11_required = xtrue; then - if test x$ac_success = xno; then - AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) - fi - else - if test x$ac_success = xno; then - HAVE_CXX11=0 - AC_MSG_NOTICE([No compiler with C++11 support was found]) - else - HAVE_CXX11=1 - AC_DEFINE(HAVE_CXX11,1, - [define if the compiler supports basic C++11 syntax]) - fi - - AC_SUBST(HAVE_CXX11) - fi -]) diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 deleted file mode 100644 index 90e91ca6..00000000 --- a/m4/ax_pthread.m4 +++ /dev/null @@ -1,310 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_pthread.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) -# -# DESCRIPTION -# -# This macro figures out how to build C programs using POSIX threads. It -# sets the PTHREAD_LIBS output variable to the threads library and linker -# flags, and the PTHREAD_CFLAGS output variable to any special C compiler -# flags that are needed. (The user can also force certain compiler -# flags/libs to be tested by setting these environment variables.) -# -# Also sets PTHREAD_CC to any special C compiler that is needed for -# multi-threaded programs (defaults to the value of CC otherwise). (This -# is necessary on AIX to use the special cc_r compiler alias.) -# -# NOTE: You are assumed to not only compile your program with these flags, -# but also link it with them as well. e.g. you should link with -# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS -# -# If you are only building threads programs, you may wish to use these -# variables in your default LIBS, CFLAGS, and CC: -# -# LIBS="$PTHREAD_LIBS $LIBS" -# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" -# CC="$PTHREAD_CC" -# -# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant -# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name -# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). -# -# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the -# PTHREAD_PRIO_INHERIT symbol is defined when compiling with -# PTHREAD_CFLAGS. -# -# ACTION-IF-FOUND is a list of shell commands to run if a threads library -# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it -# is not found. If ACTION-IF-FOUND is not specified, the default action -# will define HAVE_PTHREAD. -# -# Please let the authors know if this macro fails on any platform, or if -# you have any other suggestions or comments. This macro was based on work -# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help -# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by -# Alejandro Forero Cuervo to the autoconf macro repository. We are also -# grateful for the helpful feedback of numerous users. -# -# Updated for Autoconf 2.68 by Daniel Richard G. -# -# LICENSE -# -# Copyright (c) 2008 Steven G. Johnson -# Copyright (c) 2011 Daniel Richard G. -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 17 - -AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) -AC_DEFUN([AX_PTHREAD], [ -AC_REQUIRE([AC_CANONICAL_HOST]) -AC_LANG_PUSH([C++]) -ax_pthread_ok=no - -# We used to check for pthread.h first, but this fails if pthread.h -# requires special compiler flags (e.g. on True64 or Sequent). -# It gets checked for in the link test anyway. - -# First of all, check if the user has set any of the PTHREAD_LIBS, -# etcetera environment variables, and if threads linking works using -# them: -if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then - save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CXXFLAGS=$PTHREAD_CFLAGS]) - AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) - AC_MSG_RESULT($ax_pthread_ok) - if test x"$ax_pthread_ok" = xno; then - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" - fi - LIBS="$save_LIBS" - CXXFLAGS="$save_CXXFLAGS" -fi - -# We must check for the threads library under a number of different -# names; the ordering is very important because some systems -# (e.g. DEC) have both -lpthread and -lpthreads, where one of the -# libraries is broken (non-POSIX). - -# Create a list of thread flags to try. Items starting with a "-" are -# C compiler flags, and other items are library names, except for "none" -# which indicates that we try without any flags at all, and "pthread-config" -# which is a program returning the flags for the Pth emulation library. - -ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" - -# The ordering *is* (sometimes) important. Some notes on the -# individual items follow: - -# pthreads: AIX (must check this before -lpthread) -# none: in case threads are in libc; should be tried before -Kthread and -# other compiler flags to prevent continual compiler warnings -# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) -# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) -# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) -# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) -# -pthreads: Solaris/gcc -# -mthreads: Mingw32/gcc, Lynx/gcc -# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it -# doesn't hurt to check since this sometimes defines pthreads too; -# also defines -D_REENTRANT) -# ... -mt is also the pthreads flag for HP/aCC -# pthread: Linux, etcetera -# --thread-safe: KAI C++ -# pthread-config: use pthread-config program (for GNU Pth library) - -case "${host_cpu}-${host_os}" in - *solaris*) - - # On Solaris (at least, for some versions), libc contains stubbed - # (non-functional) versions of the pthreads routines, so link-based - # tests will erroneously succeed. (We need to link with -pthreads/-mt/ - # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather - # a function called by this macro, so we could check for that, but - # who knows whether they'll stub that too in a future libc.) So, - # we'll just look for -pthreads and -lpthread first: - - ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" - ;; - - *-darwin*) - ax_pthread_flags="-pthread $ax_pthread_flags" - ;; -esac - -if test x"$ax_pthread_ok" = xno; then -for flag in $ax_pthread_flags; do - - case $flag in - none) - AC_MSG_CHECKING([whether pthreads work without any flags]) - ;; - - -*) - AC_MSG_CHECKING([whether pthreads work with $flag]) - PTHREAD_CFLAGS="$flag" - PTHREAD_LIBS="$flag" - ;; - - pthread-config) - AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) - if test x"$ax_pthread_config" = xno; then continue; fi - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; - - *) - AC_MSG_CHECKING([for the pthreads library -l$flag]) - PTHREAD_LIBS="-l$flag" - ;; - esac - - save_LIBS="$LIBS" - save_CXXFLAGS="$CXXFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" - - # Check for various functions. We must include pthread.h, - # since some functions may be macros. (On the Sequent, we - # need a special flag -Kthread to make this header compile.) - # We check for pthread_join because it is in -lpthread on IRIX - # while pthread_create is in libc. We check for pthread_attr_init - # due to DEC craziness with -lpthreads. We check for - # pthread_cleanup_push because it is one of the few pthread - # functions on Solaris that doesn't have a non-functional libc stub. - # We try pthread_create on general principles. - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include - static void routine(void *a) { *((int*)a) = 42; } - static void *start_routine(void *a) { return a; }], - [pthread_t th; pthread_attr_t attr; - pthread_create(&th, 0, start_routine, 0); - pthread_join(th, 0); - pthread_attr_init(&attr); - pthread_cleanup_push(routine, 0); - pthread_cleanup_pop(0) /* ; */])], - [ax_pthread_ok=yes], - []) - - LIBS="$save_LIBS" - CXXFLAGS="$save_CXXFLAGS" - - AC_MSG_RESULT($ax_pthread_ok) - if test "x$ax_pthread_ok" = xyes; then - break; - fi - - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" -done -fi - -# Various other checks: -if test "x$ax_pthread_ok" = xyes; then - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS" - - # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. - AC_MSG_CHECKING([for joinable pthread attribute]) - attr_name=unknown - for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], - [int attr = $attr; return attr /* ; */])], - [attr_name=$attr; break], - []) - done - AC_MSG_RESULT($attr_name) - if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then - AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, - [Define to necessary symbol if this constant - uses a non-standard name on your system.]) - fi - - AC_MSG_CHECKING([if more special flags are required for pthreads]) - flag=no - case "${host_cpu}-${host_os}" in - *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; - *-osf* | *-hpux*) flag="-D_REENTRANT";; - *solaris*) - if test "$GXX" = "yes"; then - flag="-D_REENTRANT" - else - flag="-mt -D_REENTRANT" - fi - ;; - esac - AC_MSG_RESULT(${flag}) - if test "x$flag" != xno; then - PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" - fi - - AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], - ax_cv_PTHREAD_PRIO_INHERIT, [ - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT; (void) i;]])], - [ax_cv_PTHREAD_PRIO_INHERIT=yes], - [ax_cv_PTHREAD_PRIO_INHERIT=no]) - ]) - AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], - AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) - - LIBS="$save_LIBS" - CXXFLAGS="$save_CXXFLAGS" - - # More AIX lossage: must compile with xlc_r or cc_r - if test x"$GXX" != xyes; then - AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) - else - PTHREAD_CC=$CC - fi -else - PTHREAD_CC="$CC" -fi - -AC_SUBST(PTHREAD_LIBS) -AC_SUBST(PTHREAD_CFLAGS) -AC_SUBST(PTHREAD_CC) - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test x"$ax_pthread_ok" = xyes; then - ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) - : -else - ax_pthread_ok=no - $2 -fi -AC_LANG_POP -])dnl AX_PTHREAD diff --git a/m4/iconv.m4 b/m4/iconv.m4 deleted file mode 100644 index 8424c9bc..00000000 --- a/m4/iconv.m4 +++ /dev/null @@ -1,272 +0,0 @@ -# iconv.m4 serial 18 (gettext-0.18.2) -dnl Copyright (C) 2000-2002, 2007-2012 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl From Bruno Haible. - -AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], -[ - dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. - AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) -dnl AC_REQUIRE([AC_LIB_RPATH]) - - dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV - dnl accordingly. - AC_LIB_LINKFLAGS_BODY([iconv]) -]) - -AC_DEFUN([AM_ICONV_LINK], -[ - [HAVE_ICONV=""] - - dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and - dnl those with the standalone portable GNU libiconv installed). - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - - dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV - dnl accordingly. - AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) - - dnl Add $INCICONV to CPPFLAGS before performing the following checks, - dnl because if the user has installed libiconv and not disabled its use - dnl via --without-libiconv-prefix, he wants to use it. The first - dnl AC_LINK_IFELSE will then fail, the second AC_LINK_IFELSE will succeed. - am_save_CPPFLAGS="$CPPFLAGS" - AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) - - AC_CACHE_CHECK([for iconv], [am_cv_func_iconv], [ - am_cv_func_iconv="no, consider installing GNU libiconv" - am_cv_lib_iconv=no - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include -#include - ]], - [[iconv_t cd = iconv_open("",""); - iconv(cd,NULL,NULL,NULL,NULL); - iconv_close(cd);]])], - [am_cv_func_iconv=yes]) - if test "$am_cv_func_iconv" != yes; then - am_save_LIBS="$LIBS" - LIBS="$LIBS $LIBICONV" - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include -#include - ]], - [[iconv_t cd = iconv_open("",""); - iconv(cd,NULL,NULL,NULL,NULL); - iconv_close(cd);]])], - [am_cv_lib_iconv=yes] - [am_cv_func_iconv=yes]) - LIBS="$am_save_LIBS" - fi - ]) - if test "$am_cv_func_iconv" = yes; then - AC_CACHE_CHECK([for working iconv], [am_cv_func_iconv_works], [ - dnl This tests against bugs in AIX 5.1, AIX 6.1..7.1, HP-UX 11.11, - dnl Solaris 10. - am_save_LIBS="$LIBS" - if test $am_cv_lib_iconv = yes; then - LIBS="$LIBS $LIBICONV" - fi - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -int main () -{ - int result = 0; - /* Test against AIX 5.1 bug: Failures are not distinguishable from successful - returns. */ - { - iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); - if (cd_utf8_to_88591 != (iconv_t)(-1)) - { - static const char input[] = "\342\202\254"; /* EURO SIGN */ - char buf[10]; - const char *inptr = input; - size_t inbytesleft = strlen (input); - char *outptr = buf; - size_t outbytesleft = sizeof (buf); - size_t res = iconv (cd_utf8_to_88591, - (char **) &inptr, &inbytesleft, - &outptr, &outbytesleft); - if (res == 0) - result |= 1; - iconv_close (cd_utf8_to_88591); - } - } - /* Test against Solaris 10 bug: Failures are not distinguishable from - successful returns. */ - { - iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); - if (cd_ascii_to_88591 != (iconv_t)(-1)) - { - static const char input[] = "\263"; - char buf[10]; - const char *inptr = input; - size_t inbytesleft = strlen (input); - char *outptr = buf; - size_t outbytesleft = sizeof (buf); - size_t res = iconv (cd_ascii_to_88591, - (char **) &inptr, &inbytesleft, - &outptr, &outbytesleft); - if (res == 0) - result |= 2; - iconv_close (cd_ascii_to_88591); - } - } - /* Test against AIX 6.1..7.1 bug: Buffer overrun. */ - { - iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1"); - if (cd_88591_to_utf8 != (iconv_t)(-1)) - { - static const char input[] = "\304"; - static char buf[2] = { (char)0xDE, (char)0xAD }; - const char *inptr = input; - size_t inbytesleft = 1; - char *outptr = buf; - size_t outbytesleft = 1; - size_t res = iconv (cd_88591_to_utf8, - (char **) &inptr, &inbytesleft, - &outptr, &outbytesleft); - if (res != (size_t)(-1) || outptr - buf > 1 || buf[1] != (char)0xAD) - result |= 4; - iconv_close (cd_88591_to_utf8); - } - } -#if 0 /* This bug could be worked around by the caller. */ - /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ - { - iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); - if (cd_88591_to_utf8 != (iconv_t)(-1)) - { - static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; - char buf[50]; - const char *inptr = input; - size_t inbytesleft = strlen (input); - char *outptr = buf; - size_t outbytesleft = sizeof (buf); - size_t res = iconv (cd_88591_to_utf8, - (char **) &inptr, &inbytesleft, - &outptr, &outbytesleft); - if ((int)res > 0) - result |= 8; - iconv_close (cd_88591_to_utf8); - } - } -#endif - /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is - provided. */ - if (/* Try standardized names. */ - iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) - /* Try IRIX, OSF/1 names. */ - && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) - /* Try AIX names. */ - && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) - /* Try HP-UX names. */ - && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) - result |= 16; - return result; -}]])], - [am_cv_func_iconv_works=yes], - [am_cv_func_iconv_works=no], - [ -changequote(,)dnl - case "$host_os" in - aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; - *) am_cv_func_iconv_works="guessing yes" ;; - esac -changequote([,])dnl - ]) - LIBS="$am_save_LIBS" - ]) - case "$am_cv_func_iconv_works" in - *no) am_func_iconv=no am_cv_lib_iconv=no ;; - *) am_func_iconv=yes ;; - esac - else - am_func_iconv=no am_cv_lib_iconv=no - fi - if test "$am_func_iconv" = yes; then - AC_DEFINE([HAVE_ICONV], [1], - [Define if you have the iconv() function and it works.]) - [HAVE_ICONV=1] - fi - if test "$am_cv_lib_iconv" = yes; then - AC_MSG_CHECKING([how to link with libiconv]) - AC_MSG_RESULT([$LIBICONV]) - else - dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV - dnl either. - CPPFLAGS="$am_save_CPPFLAGS" - LIBICONV= - LTLIBICONV= - fi - AC_SUBST([HAVE_ICONV]) - AC_SUBST([LIBICONV]) - AC_SUBST([LTLIBICONV]) -]) - -dnl Define AM_ICONV using AC_DEFUN_ONCE for Autoconf >= 2.64, in order to -dnl avoid warnings like -dnl "warning: AC_REQUIRE: `AM_ICONV' was expanded before it was required". -dnl This is tricky because of the way 'aclocal' is implemented: -dnl - It requires defining an auxiliary macro whose name ends in AC_DEFUN. -dnl Otherwise aclocal's initial scan pass would miss the macro definition. -dnl - It requires a line break inside the AC_DEFUN_ONCE and AC_DEFUN expansions. -dnl Otherwise aclocal would emit many "Use of uninitialized value $1" -dnl warnings. -m4_define([gl_iconv_AC_DEFUN], - m4_version_prereq([2.64], - [[AC_DEFUN_ONCE( - [$1], [$2])]], - [m4_ifdef([gl_00GNULIB], - [[AC_DEFUN_ONCE( - [$1], [$2])]], - [[AC_DEFUN( - [$1], [$2])]])])) -gl_iconv_AC_DEFUN([AM_ICONV], -[ - AM_ICONV_LINK - if test "$am_cv_func_iconv" = yes; then - AC_MSG_CHECKING([for iconv declaration]) - AC_CACHE_VAL([am_cv_proto_iconv], [ - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include -#include -extern -#ifdef __cplusplus -"C" -#endif -#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) -size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); -#else -size_t iconv(); -#endif - ]], - [[]])], - [am_cv_proto_iconv_arg1=""], - [am_cv_proto_iconv_arg1="const"]) - am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) - am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` - AC_MSG_RESULT([ - $am_cv_proto_iconv]) - AC_DEFINE_UNQUOTED([ICONV_CONST], [$am_cv_proto_iconv_arg1], - [Define as const if the declaration of iconv() needs const.]) - dnl Also substitute ICONV_CONST in the gnulib generated . - m4_ifdef([gl_ICONV_H_DEFAULTS], - [AC_REQUIRE([gl_ICONV_H_DEFAULTS]) - if test -n "$am_cv_proto_iconv_arg1"; then - ICONV_CONST="const" - fi - ]) - fi -]) diff --git a/m4/znc_visibility.m4 b/m4/znc_visibility.m4 deleted file mode 100644 index 2c642eff..00000000 --- a/m4/znc_visibility.m4 +++ /dev/null @@ -1,88 +0,0 @@ -# visibility.m4 serial 3 (gettext-0.18) -dnl Copyright (C) 2005, 2008-2010 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl From Bruno Haible. - -dnl Changes done by Uli Schlachter (C) 2011: -dnl - Renamed everything from gl_ to znc_ -dnl - Instead of using CFLAGS, this now uses CXXFLAGS (the macro would actually -dnl silently break if you called AC_LANG with anything but C before it) -dnl - Because of the above, this now requiers AC_PROG_CXX and $GXX instead -dnl of $GCC -dnl - Added calls to AC_LANG_PUSH([C++]) and AC_LANG_POP -dnl - Replaced AC_TRY_COMPILE with AC_COMPILE_IFELSE -dnl - Added a definition for dummyfunc() so that this works with -dnl -Wmissing-declarations - -dnl Tests whether the compiler supports the command-line option -dnl -fvisibility=hidden and the function and variable attributes -dnl __attribute__((__visibility__("hidden"))) and -dnl __attribute__((__visibility__("default"))). -dnl Does *not* test for __visibility__("protected") - which has tricky -dnl semantics (see the 'vismain' test in glibc) and does not exist e.g. on -dnl MacOS X. -dnl Does *not* test for __visibility__("internal") - which has processor -dnl dependent semantics. -dnl Does *not* test for #pragma GCC visibility push(hidden) - which is -dnl "really only recommended for legacy code". -dnl Set the variable CFLAG_VISIBILITY. -dnl Defines and sets the variable HAVE_VISIBILITY. - -AC_DEFUN([ZNC_VISIBILITY], -[ - AC_REQUIRE([AC_PROG_CXX]) - AC_LANG_PUSH([C++]) - CFLAG_VISIBILITY= - HAVE_VISIBILITY=0 - if test -n "$GXX"; then - dnl First, check whether -Werror can be added to the command line, or - dnl whether it leads to an error because of some other option that the - dnl user has put into $CC $CFLAGS $CPPFLAGS. - AC_MSG_CHECKING([whether the -Werror option is usable]) - AC_CACHE_VAL([znc_cv_cc_vis_werror], [ - znc_save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -Werror" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])], - [znc_cv_cc_vis_werror=yes], - [znc_cv_cc_vis_werror=no]) - CXXFLAGS="$znc_save_CXXFLAGS"]) - AC_MSG_RESULT([$znc_cv_cc_vis_werror]) - dnl Now check whether visibility declarations are supported. - AC_MSG_CHECKING([for simple visibility declarations]) - AC_CACHE_VAL([znc_cv_cc_visibility], [ - znc_save_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -fvisibility=hidden" - dnl We use the option -Werror and a function dummyfunc, because on some - dnl platforms (Cygwin 1.7) the use of -fvisibility triggers a warning - dnl "visibility attribute not supported in this configuration; ignored" - dnl at the first function definition in every compilation unit, and we - dnl don't want to use the option in this case. - if test $znc_cv_cc_vis_werror = yes; then - CXXFLAGS="$CXXFLAGS -Werror" - fi - AC_COMPILE_IFELSE([AC_LANG_PROGRAM( - [[extern __attribute__((__visibility__("hidden"))) int hiddenvar; - extern __attribute__((__visibility__("default"))) int exportedvar; - extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void); - extern __attribute__((__visibility__("default"))) int exportedfunc (void); - void dummyfunc (void); - void dummyfunc (void) {}]], - [])], - [znc_cv_cc_visibility=yes], - [znc_cv_cc_visibility=no]) - CXXFLAGS="$znc_save_CXXFLAGS"]) - AC_MSG_RESULT([$znc_cv_cc_visibility]) - if test $znc_cv_cc_visibility = yes; then - CFLAG_VISIBILITY="-fvisibility=hidden" - HAVE_VISIBILITY=1 - fi - fi - AC_SUBST([CFLAG_VISIBILITY]) - AC_SUBST([HAVE_VISIBILITY]) - AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY], - [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.]) - AC_LANG_POP -]) diff --git a/make-tarball.sh b/make-tarball.sh index f0a3f824..a96a1970 100755 --- a/make-tarball.sh +++ b/make-tarball.sh @@ -16,6 +16,7 @@ if [ "x$1" = "x--nightly" ]; then TARGZ=$3 SIGN=0 DESC=-nightly-`date +%Y%m%d`-`git $GITDIR rev-parse HEAD | cut -b-8` + NIGHTLY=1 else VERSION=$1 if [ "x$VERSION" = "x" ] ; then @@ -32,8 +33,8 @@ else ZNCDIR=znc-$VERSION TARGZ=$ZNCDIR.tar.gz SIGN=1 - DESC="" - # DESC="-rc1" + DESC="$(sed -En 's/set\(alpha_version "(.*)"\).*/\1/p' CMakeLists.txt)" + NIGHTLY=0 fi TARGZ=`readlink -f -- $TARGZ` @@ -42,6 +43,8 @@ echo "Exporting . to $TMPDIR/$ZNCDIR..." git checkout-index --all --prefix=$TMPDIR/$ZNCDIR/ mkdir -p --mode=0755 $TMPDIR/$ZNCDIR/third_party/Csocket cp -p third_party/Csocket/Csocket.cc third_party/Csocket/Csocket.h $TMPDIR/$ZNCDIR/third_party/Csocket/ +mkdir -p --mode=0755 $TMPDIR/$ZNCDIR/third_party/cctz +cp -Rp third_party/cctz/src third_party/cctz/include third_party/cctz/LICENSE.txt $TMPDIR/$ZNCDIR/third_party/cctz/ ( cd $TMPDIR2 cmake $TMPDIR/$ZNCDIR -DWANT_PERL=yes -DWANT_PYTHON=yes @@ -49,17 +52,12 @@ cp -p third_party/Csocket/Csocket.cc third_party/Csocket/Csocket.h $TMPDIR/$ZNCD ) ( cd $TMPDIR/$ZNCDIR - AUTOMAKE_FLAGS="--add-missing --copy" ./autogen.sh - rm -r autom4te.cache/ - rm .travis* .appveyor* + rm -rf .travis* .appveyor* .ci/ .github/ rm make-tarball.sh - # For autoconf - sed -e "s/THIS_IS_NOT_TARBALL//" -i Makefile.in - echo '#include ' > src/version.cpp - echo "const char* ZNC_VERSION_EXTRA = VERSION_EXTRA \"$DESC\";" >> src/version.cpp - # For cmake if [ "x$DESC" != "x" ]; then - echo $DESC > .nightly + if [ $NIGHTLY = 1 ]; then + echo $DESC > .nightly + fi fi ) ( diff --git a/man/Makefile.in b/man/Makefile.in deleted file mode 100644 index 358ca0f7..00000000 --- a/man/Makefile.in +++ /dev/null @@ -1,44 +0,0 @@ -SHELL := @SHELL@ - -# Support out-of-tree builds -VPATH := @srcdir@ - -prefix := @prefix@ -exec_prefix := @exec_prefix@ -datarootdir := @datarootdir@ -mandir := @mandir@ - -INSTALL := @INSTALL@ -INSTALL_DATA := @INSTALL_DATA@ - -MAN1 := znc.1.gz znc-buildmod.1.gz - -ifneq "$(V)" "" -VERBOSE=1 -endif -ifeq "$(VERBOSE)" "" -Q=@ -E=@echo -else -Q= -E=@\# -endif - -all: $(MAN1) - -%.1.gz: %.1 Makefile - $(E) Packing man page $@... - $(Q)gzip -9 <$< >$@ - -clean: - -rm -f $(MAN1) - -install: $(MAN1) - test -d $(DESTDIR)$(mandir)/man1 || $(INSTALL) -d $(DESTDIR)$(mandir)/man1 - $(INSTALL_DATA) $(MAN1) $(DESTDIR)$(mandir)/man1 - -uninstall: - for file in $(MAN1) ; do \ - rm $(DESTDIR)$(mandir)/man1/$$file || exit 1 ; \ - done - rmdir $(DESTDIR)$(mandir)/man1 diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 5307c5e4..e1a84799 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. +# Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,8 +24,7 @@ function(add_cxx_module mod modpath) PREFIX "" SUFFIX ".so" NO_SONAME true - CXX_VISIBILITY_PRESET "hidden" - COMPILE_DEFINITIONS "znc_export_lib_EXPORTS") + CXX_VISIBILITY_PRESET "hidden") if(moddef_${mod}) target_compile_definitions("module_${mod}" ${moddef_${mod}}) endif() @@ -38,11 +37,7 @@ function(add_cxx_module mod modpath) if(moddepend_${mod}) add_dependencies("module_${mod}" ${moddepend_${mod}}) endif() - # ${znclib_LIB_DEPENDS} is to overcome OSX's need for -undefined suppress - # when accessing symbols exported by dependencies of znclib (e.g. - # openssl), but not used in znclib itself - target_link_libraries("module_${mod}" PRIVATE ${znc_link} ${modlink_${mod}} - ${znclib_LIB_DEPENDS}) + target_link_libraries("module_${mod}" PRIVATE ZNC ${modlink_${mod}}) set_target_properties("module_${mod}" PROPERTIES "" "" ${modprop_${mod}}) install(TARGETS "module_${mod}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/znc") @@ -55,8 +50,7 @@ function(add_python_module mod modpath) endfunction() if(CYRUS_FOUND) - set(modcompile_cyrusauth PRIVATE ${CYRUS_CFLAGS}) - set(modlink_cyrusauth ${CYRUS_LDFLAGS}) + set(modlink_cyrusauth PkgConfig::CYRUS) else() set(moddisable_cyrusauth true) endif() @@ -67,7 +61,7 @@ else() set(moddisable_modperl true) endif() -if(PYTHON_FOUND) +if(Python3_FOUND) add_subdirectory(modpython) else() set(moddisable_modpython true) @@ -91,8 +85,6 @@ foreach(modpath ${all_modules}) continue() endif() - list(APPEND actual_modules "${modpath}") - set(modenabled true) if(moddisable_${mod}) @@ -110,14 +102,16 @@ foreach(modpath ${all_modules}) if(modenabled) if(modtype STREQUAL "cpp") add_cxx_module("${mod}" "${modpath}") - endif() - if(modtype STREQUAL "pm") + elseif(modtype STREQUAL "pm") add_perl_module("${mod}" "${modpath}") - endif() - if(modtype STREQUAL "py") + elseif(modtype STREQUAL "py") add_python_module("${mod}" "${modpath}") + else() + continue() endif() endif() + + list(APPEND actual_modules "${modpath}") endforeach() if(HAVE_I18N) diff --git a/modules/Makefile.in b/modules/Makefile.in deleted file mode 100644 index 88ac4fe3..00000000 --- a/modules/Makefile.in +++ /dev/null @@ -1,146 +0,0 @@ -# -# Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -all: - -SHELL := @SHELL@ - -# Support out-of-tree builds -srcdir := @srcdir@ -VPATH := @srcdir@ - -prefix := @prefix@ -exec_prefix := @exec_prefix@ -datarootdir := @datarootdir@ -bindir := @bindir@ -datadir := @datadir@ -sysconfdir := @sysconfdir@ -libdir := @libdir@ -sbindir := @sbindir@ -localstatedir := @localstatedir@ -CXX := @CXX@ -# CXXFLAGS are for the main binary, so don't use them here, use MODFLAGS instead -MODFLAGS := -I$(srcdir)/../include -I../include @CPPFLAGS@ @MODFLAGS@ -MODLINK := @MODLINK@ -LDFLAGS := @LDFLAGS@ -ISCYGWIN := @ISCYGWIN@ - -# LIBS are not and should not be used in here. -# The znc binary links already against those. -# ...but not on cygwin! -LIBS := -ifeq "$(ISCYGWIN)" "1" -LIBS += @LIBS@ -endif - -PERL_ON := @PERL@ -PERL := @PERL_BINARY@ -PYTHON_ON:= @PYTHON@ -PY_CFLAGS:= @python_CFLAGS@ -PY_LDFLAGS:=@python_LIBS@ -SWIG := @SWIG@ -MODDIR := @MODDIR@ -DATADIR := @DATADIR@ -LIBZNC := @LIBZNC@ -LIBZNCDIR:= @LIBZNCDIR@ -INSTALL := @INSTALL@ -INSTALL_PROGRAM := @INSTALL_PROGRAM@ -INSTALL_SCRIPT := @INSTALL_SCRIPT@ -INSTALL_DATA := @INSTALL_DATA@ -SED := @SED@ - -TCL_FLAGS:= @TCL_FLAGS@ - -ifneq "$(V)" "" -VERBOSE=1 -endif -ifeq "$(VERBOSE)" "" -Q=@ -E=@echo -C=-s -else -Q= -E=@\# -C= -endif - -ifneq "$(LIBZNC)" "" -LIBS += -L.. -lznc -Wl,-rpath,$(LIBZNCDIR) -endif - -CLEAN := - -FILES := $(notdir $(wildcard $(srcdir)/*.cpp)) - -include $(srcdir)/modperl/Makefile.inc -include $(srcdir)/modpython/Makefile.inc -include $(srcdir)/modtcl/Makefile.inc - -FILES := $(basename $(FILES)) - -ifeq "@NOSSL@" "1" -FILES := $(foreach file, $(FILES), \ - $(if $(shell grep REQUIRESSL $(srcdir)/$(file).cpp), \ - , \ - $(basename $(file)) \ - )) -endif - -ifeq "@CYRUS@" "" -FILES := $(shell echo $(FILES) | sed -e "s:cyrusauth::") -endif -cyrusauthLDFLAGS := -lsasl2 - -TARGETS := $(addsuffix .so, $(sort $(FILES))) - -CLEAN += *.so *.o - -.PHONY: all clean install install_datadir uninstall -.SECONDARY: - -all: $(TARGETS) - -install: all install_datadir - $(INSTALL_PROGRAM) $(TARGETS) $(DESTDIR)$(MODDIR) - -install_datadir: - rm -rf $(DESTDIR)$(DATADIR)/modules - test -d $(DESTDIR)$(MODDIR) || $(INSTALL) -d $(DESTDIR)$(MODDIR) - test -d $(DESTDIR)$(DATADIR)/modules || $(INSTALL) -d $(DESTDIR)$(DATADIR)/modules - rm -rf $(DESTDIR)$(MODDIR)/*.so - cp -R $(srcdir)/data/* $(DESTDIR)$(DATADIR)/modules - find $(DESTDIR)$(DATADIR)/modules -type d -exec chmod 0755 '{}' \; - find $(DESTDIR)$(DATADIR)/modules -type f -exec chmod 0644 '{}' \; - -clean: - rm -rf $(CLEAN) - -%.o: %.cpp Makefile - @mkdir -p .depend - $(E) Building module $(notdir $(basename $@))... - $(Q)$(CXX) $(MODFLAGS) -c -o $@ $< $($(notdir $(basename $@))CXXFLAGS) -MD -MF .depend/$(notdir $@).dep - -%.so: %.o Makefile - $(E) "Linking module" $(notdir $(basename $@))... - $(Q)$(CXX) $(MODFLAGS) $(LDFLAGS) $(MODLINK) -o $@ $< $($(notdir $(basename $@))LDFLAGS) $(LIBS) - -uninstall: - # Yes, we are lazy, just remove everything in there - rm -rf $(DESTDIR)$(MODDIR)/* - rm -rf $(DESTDIR)$(DATADIR)/* - rmdir $(DESTDIR)$(MODDIR) - rmdir $(DESTDIR)$(DATADIR) - --include $(wildcard .depend/*.dep) diff --git a/modules/admindebug.cpp b/modules/admindebug.cpp new file mode 100644 index 00000000..14540b31 --- /dev/null +++ b/modules/admindebug.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +class CAdminDebugMod : public CModule { + private: + CString m_sEnabledBy; + + public: + MODCONSTRUCTOR(CAdminDebugMod) { + AddHelpCommand(); + AddCommand("Enable", "", t_d("Enable Debug Mode"), + [=](const CString& sLine) { CommandEnable(sLine); }); + AddCommand("Disable", "", t_d("Disable Debug Mode"), + [=](const CString& sLine) { CommandDisable(sLine); }); + AddCommand("Status", "", t_d("Show the Debug Mode status"), + [=](const CString& sLine) { CommandStatus(sLine); }); + } + + void CommandEnable(const CString& sCommand) { + if (!GetUser()->IsAdmin()) { + PutModule(t_s("Access denied!")); + return; + } + + ToggleDebug(true, GetUser()->GetNick()); + } + + void CommandDisable(const CString& sCommand) { + if (!GetUser()->IsAdmin()) { + PutModule(t_s("Access denied!")); + return; + } + + ToggleDebug(false, m_sEnabledBy); + } + + bool ToggleDebug(bool bEnable, const CString& sEnabledBy) { + if (!CDebug::StdoutIsTTY()) { + PutModule(t_s("Failure. We need to be running with a TTY. (is ZNC running with --foreground?)")); + return false; + } + + bool bValue = CDebug::Debug(); + + if (bEnable == bValue) { + if (bEnable) { + PutModule(t_s("Already enabled.")); + } else { + PutModule(t_s("Already disabled.")); + } + return false; + } + + CDebug::SetDebug(bEnable); + CString sEnabled = bEnable ? "on" : "off"; + CZNC::Get().Broadcast( + "An administrator has just turned Debug Mode \02" + sEnabled + + "\02. It was enabled by \02" + sEnabledBy + "\02."); + if (bEnable) { + CZNC::Get().Broadcast( + "Messages, credentials, and other sensitive data may become " + "exposed to the host during this period."); + m_sEnabledBy = sEnabledBy; + } else { + m_sEnabledBy = ""; + } + + return true; + } + + void CommandStatus(const CString& sCommand) { + if (CDebug::Debug()) { + PutModule(t_s("Debugging mode is \02on\02.")); + } else { + PutModule(t_s("Debugging mode is \02off\02.")); + } + PutModule(t_s("Logging to: \02stdout\02.")); + } +}; + +template <> +void TModInfo(CModInfo& Info) { + Info.SetWikiPage("admindebug"); +} + +GLOBALMODULEDEFS(CAdminDebugMod, t_s("Enable Debug mode dynamically.")) diff --git a/modules/adminlog.cpp b/modules/adminlog.cpp index 20f1641a..3947d95d 100644 --- a/modules/adminlog.cpp +++ b/modules/adminlog.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,39 +58,37 @@ class CAdminLogMod : public CModule { } void OnIRCConnected() override { - Log("[" + GetUser()->GetUserName() + "/" + GetNetwork()->GetName() + + Log("[" + GetUser()->GetUsername() + "/" + GetNetwork()->GetName() + "] connected to IRC: " + GetNetwork()->GetCurrentServer()->GetName()); } void OnIRCDisconnected() override { - Log("[" + GetUser()->GetUserName() + "/" + GetNetwork()->GetName() + + Log("[" + GetUser()->GetUsername() + "/" + GetNetwork()->GetName() + "] disconnected from IRC"); } - EModRet OnRaw(CString& sLine) override { - if (sLine.StartsWith("ERROR ")) { + EModRet OnRawMessage(CMessage& Message) override { + if (Message.GetCommand().Equals("ERROR")) { // ERROR :Closing Link: nick[24.24.24.24] (Excess Flood) // ERROR :Closing Link: nick[24.24.24.24] Killer (Local kill by // Killer (reason)) - CString sError(sLine.substr(6)); - if (sError.Left(1) == ":") sError.LeftChomp(); - Log("[" + GetUser()->GetUserName() + "/" + GetNetwork()->GetName() + + Log("[" + GetUser()->GetUsername() + "/" + GetNetwork()->GetName() + "] disconnected from IRC: " + GetNetwork()->GetCurrentServer()->GetName() + " [" + - sError + "]", + Message.GetParamsColon(0) + "]", LOG_NOTICE); } return CONTINUE; } void OnClientLogin() override { - Log("[" + GetUser()->GetUserName() + "] connected to ZNC from " + + Log("[" + GetUser()->GetUsername() + "] connected to ZNC from " + GetClient()->GetRemoteIP()); } void OnClientDisconnect() override { - Log("[" + GetUser()->GetUserName() + "] disconnected from ZNC from " + + Log("[" + GetUser()->GetUsername() + "] disconnected from ZNC from " + GetClient()->GetRemoteIP()); } diff --git a/modules/alias.cpp b/modules/alias.cpp index 7be7f08a..4b0b5a2a 100644 --- a/modules/alias.cpp +++ b/modules/alias.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/autoattach.cpp b/modules/autoattach.cpp index c7799c66..bf57d992 100644 --- a/modules/autoattach.cpp +++ b/modules/autoattach.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/autocycle.cpp b/modules/autocycle.cpp index 1cfd7e3e..7ac1b3c0 100644 --- a/modules/autocycle.cpp +++ b/modules/autocycle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,6 +83,7 @@ class CAutoCycleMod : public CModule { void OnListCommand(const CString& sLine) { CTable Table; Table.AddColumn(t_s("Channel")); + Table.SetStyle(CTable::ListStyle); for (const CString& sChan : m_vsChans) { Table.AddRow(); diff --git a/modules/autoop.cpp b/modules/autoop.cpp index f2b7e575..a5d3e764 100644 --- a/modules/autoop.cpp +++ b/modules/autoop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,7 +132,8 @@ class CAutoOpUser { bool FromString(const CString& sLine) { m_sUsername = sLine.Token(0, false, "\t"); - sLine.Token(1, false, "\t").Split(",", m_ssHostmasks); + // Trim because there was a bug which caused spaces in the hostname + sLine.Token(1, false, "\t").Trim_n().Split(",", m_ssHostmasks); m_sUserKey = sLine.Token(2, false, "\t"); sLine.Token(3, false, "\t").Split(" ", m_ssChans); @@ -374,7 +375,7 @@ class CAutoOpMod : public CModule { void OnAddMasksCommand(const CString& sLine) { CString sUser = sLine.Token(1); - CString sHostmasks = sLine.Token(2, true); + CString sHostmasks = sLine.Token(2); if (sHostmasks.empty()) { PutModule(t_s("Usage: AddMasks ,[mask] ...")); @@ -395,7 +396,7 @@ class CAutoOpMod : public CModule { void OnDelMasksCommand(const CString& sLine) { CString sUser = sLine.Token(1); - CString sHostmasks = sLine.Token(2, true); + CString sHostmasks = sLine.Token(2); if (sHostmasks.empty()) { PutModule(t_s("Usage: DelMasks ,[mask] ...")); diff --git a/modules/autoreply.cpp b/modules/autoreply.cpp index ff50a1fd..f1c7a299 100644 --- a/modules/autoreply.cpp +++ b/modules/autoreply.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * Copyright (C) 2008 Michael "Svedrin" Ziegler diese-addy@funzt-halt.net * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/modules/autovoice.cpp b/modules/autovoice.cpp index 56d56188..5b70372b 100644 --- a/modules/autovoice.cpp +++ b/modules/autovoice.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/awaynick.cpp b/modules/awaynick.cpp index e6a956ca..176c7276 100644 --- a/modules/awaynick.cpp +++ b/modules/awaynick.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/awaystore.cpp b/modules/awaystore.cpp index dc128551..a7f639b7 100644 --- a/modules/awaystore.cpp +++ b/modules/awaystore.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * Author: imaginos * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -58,8 +58,8 @@ class CAwayJob : public CTimer { class CAway : public CModule { void AwayCommand(const CString& sCommand) { CString sReason; - time_t curtime; - time(&curtime); + timeval curtime; + gettimeofday(&curtime, nullptr); if (sCommand.Token(1) != "-quiet") { sReason = CUtils::FormatTime(curtime, sCommand.Token(1, true), @@ -351,7 +351,7 @@ class CAway : public CModule { void OnClientDisconnect() override { Away(); } CString GetPath() { - CString sBuffer = GetUser()->GetUserName(); + CString sBuffer = GetUser()->GetUsername(); CString sRet = GetSavePath(); sRet += "/.znc-away-" + CBlowfish::MD5(sBuffer, true); return (sRet); diff --git a/modules/block_motd.cpp b/modules/block_motd.cpp index ad02663d..aa1e3206 100644 --- a/modules/block_motd.cpp +++ b/modules/block_motd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,22 +47,20 @@ class CBlockMotd : public CModule { } } - EModRet OnRaw(CString& sLine) override { - const CString sCmd = sLine.Token(1); - - if ((sCmd == "375" /* begin of MOTD */ || sCmd == "372" /* MOTD */) && + EModRet OnNumericMessage(CNumericMessage& Message) override { + if ((Message.GetCode() == 375 /* begin of MOTD */ || + Message.GetCode() == 372 /* MOTD */) && !ShouldTemporarilyAcceptMotd()) return HALT; - if (sCmd == "376" /* End of MOTD */) { + if (Message.GetCode() == 376 /* End of MOTD */) { if (!ShouldTemporarilyAcceptMotd()) { - sLine = sLine.Token(0) + " 422 " + sLine.Token(2) + " :" + - t_s("MOTD blocked by ZNC"); + Message.SetParam(1, t_s("MOTD blocked by ZNC")); } StopTemporarilyAcceptingMotd(); } - if (sCmd == "422") { + if (Message.GetCode() == 422) { // Server has no MOTD StopTemporarilyAcceptingMotd(); } diff --git a/modules/blockuser.cpp b/modules/blockuser.cpp index 2c9ccd35..7c154763 100644 --- a/modules/blockuser.cpp +++ b/modules/blockuser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2016 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -101,7 +101,7 @@ class CBlockUser : public CModule { return; } - if (GetUser()->GetUserName().Equals(sUser)) { + if (GetUser()->GetUsername().Equals(sUser)) { PutModule(t_s("You can't block yourself")); return; } @@ -135,13 +135,13 @@ class CBlockUser : public CModule { if (sAction == "display") { Tmpl["Blocked"] = CString(IsBlocked(Tmpl["Username"])); Tmpl["Self"] = CString(Tmpl["Username"].Equals( - WebSock.GetSession()->GetUser()->GetUserName())); + WebSock.GetSession()->GetUser()->GetUsername())); return true; } if (sAction == "change" && WebSock.GetParam("embed_blockuser_presented").ToBool()) { if (Tmpl["Username"].Equals( - WebSock.GetSession()->GetUser()->GetUserName()) && + WebSock.GetSession()->GetUser()->GetUsername()) && WebSock.GetParam("embed_blockuser_block").ToBool()) { WebSock.GetSession()->AddError( t_s("You can't block yourself")); @@ -203,7 +203,7 @@ class CBlockUser : public CModule { (*it2)->SetIRCConnectEnabled(false); } - SetNV(pUser->GetUserName(), ""); + SetNV(pUser->GetUsername(), ""); return true; } }; diff --git a/modules/bouncedcc.cpp b/modules/bouncedcc.cpp index 805a0593..db825b08 100644 --- a/modules/bouncedcc.cpp +++ b/modules/bouncedcc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/buffextras.cpp b/modules/buffextras.cpp index c3322a1e..01deb706 100644 --- a/modules/buffextras.cpp +++ b/modules/buffextras.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cert.cpp b/modules/cert.cpp index 689b6200..67484665 100644 --- a/modules/cert.cpp +++ b/modules/cert.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ class CCertMod : public CModule { "You do not have a certificate. Please use the web interface " "to add a certificate")); if (GetUser()->IsAdmin()) { - PutModule(t_f("Alternatively you can either place one at {1}")( + PutModule(t_f("Alternatively you can place one at {1}")( PemFile())); } } diff --git a/modules/certauth.cpp b/modules/certauth.cpp index 43264526..52af6636 100644 --- a/modules/certauth.cpp +++ b/modules/certauth.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,15 +53,18 @@ class CSSLClientCertMod : public CModule { for (MCString::const_iterator it = BeginNV(); it != EndNV(); ++it) { VCString vsKeys; + const CString& sUser = it->first; - if (CZNC::Get().FindUser(it->first) == nullptr) { - DEBUG("Unknown user in saved data [" + it->first + "]"); + if (CZNC::Get().FindUser(sUser) == nullptr) { + DEBUG("Unknown user in saved data [" + sUser + "]"); continue; } it->second.Split(" ", vsKeys, false); - for (const CString& sKey : vsKeys) { - m_PubKeys[it->first].insert(sKey.AsLower()); + for (CString& sKey : vsKeys) { + sKey.MakeLower(); + m_PubKeys[sUser].insert(sKey); + m_KeyToUser[sKey].insert(sUser); } } @@ -90,39 +93,70 @@ class CSSLClientCertMod : public CModule { return SaveRegistry(); } - bool AddKey(CUser* pUser, const CString& sKey) { + bool AddKey(CUser& User, CString sKey) { + sKey.MakeLower(); const pair pair = - m_PubKeys[pUser->GetUserName()].insert(sKey.AsLower()); + m_PubKeys[User.GetUsername()].insert(sKey); if (pair.second) { Save(); + m_KeyToUser[sKey].insert(User.GetUsername()); } return pair.second; } + void DelKey(CUser& User, CString sKey) { + sKey.MakeLower(); + MSCString::iterator it = m_PubKeys.find(User.GetUsername()); + if (it != m_PubKeys.end()) { + if (it->second.erase(sKey)) { + if (it->second.size() == 0) { + m_PubKeys.erase(it); + } + + it = m_KeyToUser.find(sKey); + if (it != m_KeyToUser.end()) { + it->second.erase(User.GetUsername()); + if (it->second.empty()) m_KeyToUser.erase(it); + } + + Save(); + } + } + } + EModRet OnLoginAttempt(std::shared_ptr Auth) override { const CString sUser = Auth->GetUsername(); - Csock* pSock = Auth->GetSocket(); + CZNCSock* pSock = dynamic_cast(Auth->GetSocket()); CUser* pUser = CZNC::Get().FindUser(sUser); if (pSock == nullptr || pUser == nullptr) return CONTINUE; - const CString sPubKey = GetKey(pSock); - DEBUG("User: " << sUser << " Key: " << sPubKey); + const MultiKey PubKey = GetKey(pSock); + DEBUG("User: " << sUser << " Key: " << PubKey.ToString()); - if (sPubKey.empty()) { + if (PubKey.sSHA1.empty()) { DEBUG("Peer got no public key, ignoring"); return CONTINUE; } MSCString::const_iterator it = m_PubKeys.find(sUser); if (it == m_PubKeys.end()) { - DEBUG("No saved pubkeys for this client"); + DEBUG("No saved pubkeys for this user"); return CONTINUE; } - SCString::const_iterator it2 = it->second.find(sPubKey); + SCString::const_iterator it2 = it->second.find(PubKey.sSHA1); + if (it2 != it->second.end()) { + DEBUG("Found SHA-1 pubkey, replacing with SHA-256"); + AddKey(*pUser, PubKey.sSHA256); + DelKey(*pUser, PubKey.sSHA1); + Auth->AcceptLogin(*pUser); + return HALT; + } + + it2 = it->second.find(PubKey.sSHA256); if (it2 == it->second.end()) { DEBUG("Invalid pubkey"); return CONTINUE; @@ -135,8 +169,71 @@ class CSSLClientCertMod : public CModule { return HALT; } + void OnClientGetSASLMechanisms(SCString& ssMechanisms) override { + ssMechanisms.insert("EXTERNAL"); + } + + EModRet OnClientSASLAuthenticate(const CString& sMechanism, + const CString& sMessage) override { + if (sMechanism != "EXTERNAL") { + return CONTINUE; + } + CString sUser = GetClient()->ParseUser(sMessage); + const MultiKey Key = GetKey(GetClient()); + DEBUG("Key: " << Key.ToString()); + + if (Key.sSHA1.empty()) { + GetClient()->RefuseSASLLogin("No client cert presented"); + return HALT; + } + + auto it = m_KeyToUser.find(Key.sSHA1); + if (it != m_KeyToUser.end()) { + DEBUG("Found SHA-1 pubkey for SASL, replacing with SHA-256"); + SCString ssUsers = it->second; + for (const CString& sUser2 : ssUsers) { + CUser* pUser = CZNC::Get().FindUser(sUser2); + if (pUser) { + AddKey(*pUser, Key.sSHA256); + DelKey(*pUser, Key.sSHA1); + } + } + } + + it = m_KeyToUser.find(Key.sSHA256); + if (it == m_KeyToUser.end()) { + GetClient()->RefuseSASLLogin("Client cert not recognized"); + return HALT; + } + + const SCString& ssUsers = it->second; + + if (ssUsers.empty()) { + GetClient()->RefuseSASLLogin("Key found, but list of users is empty, please report bug"); + return HALT; + } + + if (sUser.empty()) { + sUser = *ssUsers.begin(); + } else if (ssUsers.count(sUser) == 0) { + GetClient()->RefuseSASLLogin( + "The specified user doesn't have this key"); + return HALT; + } + + CUser* pUser = CZNC::Get().FindUser(sUser); + if (!pUser) { + GetClient()->RefuseSASLLogin("User not found"); + return HALT; + } + + DEBUG("Accepted cert auth for " << sUser); + GetClient()->AcceptSASLLogin(*pUser); + return HALT; + } + void HandleShowCommand(const CString& sLine) { - const CString sPubKey = GetKey(GetClient()); + const CString sPubKey = GetKey(GetClient()).sSHA256; if (sPubKey.empty()) { PutModule(t_s("You are not connected with any valid public key")); @@ -149,14 +246,14 @@ class CSSLClientCertMod : public CModule { CString sPubKey = sLine.Token(1); if (sPubKey.empty()) { - sPubKey = GetKey(GetClient()); + sPubKey = GetKey(GetClient()).sSHA256; } if (sPubKey.empty()) { PutModule( t_s("You did not supply a public key or connect with one.")); } else { - if (AddKey(GetUser(), sPubKey)) { + if (AddKey(*GetUser(), sPubKey)) { PutModule(t_f("Key '{1}' added.")(sPubKey)); } else { PutModule(t_f("The key '{1}' is already added.")(sPubKey)); @@ -169,8 +266,9 @@ class CSSLClientCertMod : public CModule { Table.AddColumn(t_s("Id", "list")); Table.AddColumn(t_s("Key", "list")); + Table.SetStyle(CTable::ListStyle); - MSCString::const_iterator it = m_PubKeys.find(GetUser()->GetUserName()); + MSCString::const_iterator it = m_PubKeys.find(GetUser()->GetUsername()); if (it == m_PubKeys.end()) { PutModule(t_s("No keys set for your user")); return; @@ -192,7 +290,7 @@ class CSSLClientCertMod : public CModule { void HandleDelCommand(const CString& sLine) { unsigned int id = sLine.Token(1, true).ToUInt(); - MSCString::iterator it = m_PubKeys.find(GetUser()->GetUserName()); + MSCString::iterator it = m_PubKeys.find(GetUser()->GetUsername()); if (it == m_PubKeys.end()) { PutModule(t_s("No keys set for your user")); @@ -210,18 +308,38 @@ class CSSLClientCertMod : public CModule { id--; } + CString sKey = *it2; it->second.erase(it2); if (it->second.size() == 0) m_PubKeys.erase(it); + + it = m_KeyToUser.find(sKey); + if (it != m_KeyToUser.end()) { + it->second.erase(GetUser()->GetUsername()); + if (it->second.empty()) m_KeyToUser.erase(it); + } + PutModule(t_s("Removed")); Save(); } - CString GetKey(Csock* pSock) { - CString sRes; - long int res = pSock->GetPeerFingerprint(sRes); + // Struct to allow transparent migration from SHA-1 to SHA-256 + struct MultiKey { + CString sSHA1; + CString sSHA256; - DEBUG("GetKey() returned status " << res << " with key " << sRes); + CString ToString() const { + return "sha-1: " + sSHA1 + ", sha-256: " + sSHA256; + } + }; + MultiKey GetKey(CZNCSock* pSock) { + MultiKey Res; + long int res = pSock->GetPeerFingerprint(Res.sSHA1); + X509* pCert = pSock->GetX509(); + Res.sSHA256 = pSock->GetSSLPeerFingerprint(pCert); + X509_free(pCert); + + DEBUG("GetKey() returned status " << res << " with key " << Res.ToString()); // This is 'inspired' by charybdis' libratbox switch (res) { @@ -229,9 +347,9 @@ class CSSLClientCertMod : public CModule { case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - return sRes.AsLower(); + return Res; default: - return ""; + return {}; } } @@ -240,9 +358,10 @@ class CSSLClientCertMod : public CModule { bool OnWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl) override { CUser* pUser = WebSock.GetSession()->GetUser(); + if (!pUser) return false; if (sPageName == "index") { - MSCString::const_iterator it = m_PubKeys.find(pUser->GetUserName()); + MSCString::const_iterator it = m_PubKeys.find(pUser->GetUsername()); if (it != m_PubKeys.end()) { for (const CString& sKey : it->second) { CTemplate& row = Tmpl.AddRow("KeyLoop"); @@ -252,21 +371,11 @@ class CSSLClientCertMod : public CModule { return true; } else if (sPageName == "add") { - AddKey(pUser, WebSock.GetParam("key")); + AddKey(*pUser, WebSock.GetParam("key")); WebSock.Redirect(GetWebPath()); return true; } else if (sPageName == "delete") { - MSCString::iterator it = m_PubKeys.find(pUser->GetUserName()); - if (it != m_PubKeys.end()) { - if (it->second.erase(WebSock.GetParam("key", false))) { - if (it->second.size() == 0) { - m_PubKeys.erase(it); - } - - Save(); - } - } - + DelKey(*pUser, WebSock.GetParam("key", false)); WebSock.Redirect(GetWebPath()); return true; } @@ -278,6 +387,7 @@ class CSSLClientCertMod : public CModule { // Maps user names to a list of allowed pubkeys typedef map> MSCString; MSCString m_PubKeys; + MSCString m_KeyToUser; }; template <> diff --git a/modules/chansaver.cpp b/modules/chansaver.cpp index e7f9e829..cc493bc1 100644 --- a/modules/chansaver.cpp +++ b/modules/chansaver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/clearbufferonmsg.cpp b/modules/clearbufferonmsg.cpp index 839dc21a..29e0ab5a 100644 --- a/modules/clearbufferonmsg.cpp +++ b/modules/clearbufferonmsg.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/clientnotify.cpp b/modules/clientnotify.cpp index aa9d34d9..0973f39b 100644 --- a/modules/clientnotify.cpp +++ b/modules/clientnotify.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,28 @@ * limitations under the License. */ -#include #include +#include using std::set; class CClientNotifyMod : public CModule { protected: CString m_sMethod; + bool m_bNewOnly{}; bool m_bOnDisconnect{}; + bool m_bNotifyOnNewIP{}; + bool m_bNotifyOnNewClientID{}; - set m_sClientsSeen; + set m_sClientsSeenIP; + set m_sClientsSeenID; void SaveSettings() { SetNV("method", m_sMethod); SetNV("newonly", m_bNewOnly ? "1" : "0"); + SetNV("notifyonnewip", m_bNotifyOnNewIP ? "1" : "0"); + SetNV("notifyonnewclientid", m_bNotifyOnNewClientID ? "1" : "0"); SetNV("ondisconnect", m_bOnDisconnect ? "1" : "0"); } @@ -48,8 +54,16 @@ class CClientNotifyMod : public CModule { t_d("Sets the notify method"), [=](const CString& sLine) { OnMethodCommand(sLine); }); AddCommand("NewOnly", t_d(""), - t_d("Turns notifications for unseen IP addresses on or off"), + t_d("Turns notifications for unseen connections on or off"), [=](const CString& sLine) { OnNewOnlyCommand(sLine); }); + AddCommand("NotifyOnNewIP", t_d(""), + t_d("Specifies whether you want to be notified about new " + "connections with new IPs"), + [=](const CString& sLine) { OnNotifyOnNewIP(sLine); }); + AddCommand("NotifyOnNewID", t_d(""), + t_d("Specifies whether you want to be notified about new " + "connections with new client IDs"), + [=](const CString& sLine) { OnNotifyOnNewID(sLine); }); AddCommand( "OnDisconnect", t_d(""), t_d("Turns notifications for clients disconnecting on or off"), @@ -68,6 +82,8 @@ class CClientNotifyMod : public CModule { // default = off for these: + m_bNotifyOnNewIP = (GetNV("notifyonnewip") == "1"); + m_bNotifyOnNewClientID = (GetNV("notifyonnewclientid") == "1"); m_bNewOnly = (GetNV("newonly") == "1"); m_bOnDisconnect = (GetNV("ondisconnect") == "1"); @@ -76,18 +92,41 @@ class CClientNotifyMod : public CModule { void OnClientLogin() override { CString sRemoteIP = GetClient()->GetRemoteIP(); - if (!m_bNewOnly || - m_sClientsSeen.find(sRemoteIP) == m_sClientsSeen.end()) { - SendNotification(t_p("", - "Another client authenticated as your user. " - "Use the 'ListClients' command to see all {1} " - "clients.", - GetUser()->GetAllClients().size())( - GetUser()->GetAllClients().size())); + CString sRemoteClientID = GetClient()->GetIdentifier(); - // the set<> will automatically disregard duplicates: - m_sClientsSeen.insert(sRemoteIP); + CString sClientNameMessage{sRemoteIP}; + if (m_bNotifyOnNewClientID && sRemoteClientID != "") { + sClientNameMessage += " / " + sRemoteClientID; } + + auto sendLoginNotification = [&]() { + SendNotification( + t_p("", + "Another client ({1}) authenticated as your user. " + "Use the 'ListClients' command to see all {2} " + "clients.", + GetUser()->GetAllClients().size())( + sClientNameMessage, GetUser()->GetAllClients().size())); + }; + + if (m_bNewOnly) { + // see if we actually got a new client + // TODO: replace setName.find(...) == setName.end() with + // !setName.contains() once ZNC uses C++20 + if ((m_bNotifyOnNewIP && (m_sClientsSeenIP.find(sRemoteIP) == + m_sClientsSeenIP.end())) || + (m_bNotifyOnNewClientID && + (m_sClientsSeenID.find(sRemoteClientID) == + m_sClientsSeenID.end()))) { + sendLoginNotification(); + } + } else { + sendLoginNotification(); + } + + // the set<> will automatically disregard duplicates: + m_sClientsSeenIP.insert(sRemoteIP); + m_sClientsSeenID.insert(sRemoteClientID); } void OnClientDisconnect() override { @@ -127,6 +166,32 @@ class CClientNotifyMod : public CModule { PutModule(t_s("Saved.")); } + void OnNotifyOnNewIP(const CString& sCommand) { + const CString sArg = sCommand.Token(1, true).AsLower(); + + if (sArg.empty()) { + PutModule(t_s("Usage: NotifyOnNewIP ")); + return; + } + + m_bNotifyOnNewIP = sArg.ToBool(); + SaveSettings(); + PutModule(t_s("Saved.")); + } + + void OnNotifyOnNewID(const CString& sCommand) { + const CString sArg = sCommand.Token(1, true).AsLower(); + + if (sArg.empty()) { + PutModule(t_s("Usage: NotifyOnNewID ")); + return; + } + + m_bNotifyOnNewClientID = sArg.ToBool(); + SaveSettings(); + PutModule(t_s("Saved.")); + } + void OnDisconnectCommand(const CString& sCommand) { const CString sArg = sCommand.Token(1, true).AsLower(); @@ -142,9 +207,11 @@ class CClientNotifyMod : public CModule { void OnShowCommand(const CString& sLine) { PutModule( - t_f("Current settings: Method: {1}, for unseen IP addresses only: " - "{2}, notify on disconnecting clients: {3}")( - m_sMethod, m_bNewOnly, m_bOnDisconnect)); + t_f("Current settings: Method: {1}, for unseen only: {2}, notify" + "for unseen IPs: {3}, notify for unseen IDs: {4}, notify on" + "disconnecting clients: {5}")( + m_sMethod, m_bNewOnly, m_bNotifyOnNewIP, m_bNotifyOnNewClientID, + m_bOnDisconnect)); } }; diff --git a/modules/controlpanel.cpp b/modules/controlpanel.cpp index 6192e5ed..a8c3f121 100644 --- a/modules/controlpanel.cpp +++ b/modules/controlpanel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * Copyright (C) 2008 by Stefan Rado * based on admin.cpp by Sebastian Ramacher * based on admin.cpp in crox branch @@ -50,6 +50,7 @@ class CAdminMod : public CModule { CTable VarTable; VarTable.AddColumn(t_s("Type", "helptable")); VarTable.AddColumn(t_s("Variables", "helptable")); + VarTable.SetStyle(CTable::ListStyle); std::map mvsTypedVariables; for (unsigned int i = 0; i != uSize; ++i) { CString sVar = CString(vars[i].name).AsLower(); @@ -77,7 +78,7 @@ class CAdminMod : public CModule { const CString str = t_s("String"); const CString boolean = t_s("Boolean (true/false)"); const CString integer = t_s("Integer"); - const CString doublenum = t_s("Double"); + const CString number = t_s("Number"); const CString sCmdFilter = sLine.Token(1, false); const CString sVarFilter = sLine.Token(2, true).AsLower(); @@ -93,6 +94,11 @@ class CAdminMod : public CModule { {"MultiClients", boolean}, {"DenyLoadMod", boolean}, {"DenySetBindHost", boolean}, + {"DenySetIdent", boolean}, + {"DenySetNetwork", boolean}, + {"DenySetRealName", boolean}, + {"DenySetQuitMsg", boolean}, + {"DenySetCTCPReplies", boolean}, {"DefaultChanModes", str}, {"QuitMsg", str}, {"ChanBufferSize", integer}, @@ -108,9 +114,11 @@ class CAdminMod : public CModule { {"Admin", boolean}, {"AppendTimestamp", boolean}, {"PrependTimestamp", boolean}, + {"AuthOnlyViaModule", boolean}, {"TimestampFormat", str}, {"DCCBindHost", str}, {"StatusPrefix", str}, + {"NoTrafficTimeout", integer}, #ifdef HAVE_I18N {"Language", str}, #endif @@ -118,6 +126,7 @@ class CAdminMod : public CModule { {"ClientEncoding", str}, #endif }; + PutModule(""); PrintVarsHelp(sVarFilter, vars, ARRAY_SIZE(vars), t_s("The following variables are available when " "using the Set/Get commands:")); @@ -131,7 +140,7 @@ class CAdminMod : public CModule { {"Ident", str}, {"RealName", str}, {"BindHost", str}, - {"FloodRate", doublenum}, + {"FloodRate", number}, {"FloodBurst", integer}, {"JoinDelay", integer}, #ifdef HAVE_ICU @@ -141,6 +150,7 @@ class CAdminMod : public CModule { {"TrustAllCerts", boolean}, {"TrustPKI", boolean}, }; + PutModule(""); PrintVarsHelp(sVarFilter, nvars, ARRAY_SIZE(nvars), t_s("The following variables are available when " "using the SetNetwork/GetNetwork commands:")); @@ -154,15 +164,18 @@ class CAdminMod : public CModule { {"InConfig", boolean}, {"AutoClearChanBuffer", boolean}, {"Detached", boolean}}; + PutModule(""); PrintVarsHelp(sVarFilter, cvars, ARRAY_SIZE(cvars), t_s("The following variables are available when " "using the SetChan/GetChan commands:")); } - if (sCmdFilter.empty()) + if (sCmdFilter.empty()) { + PutModule(""); PutModule( t_s("You can use $user as the user name and $network as the " "network name for modifying your own user and network.")); + } } CUser* FindUser(const CString& sUsername) { @@ -194,7 +207,7 @@ class CAdminMod : public CModule { if (!pNetwork) { PutModule( t_f("Error: User {1} does not have a network named [{2}].")( - pUser->GetUserName(), sNetwork)); + pUser->GetUsername(), sNetwork)); } return pNetwork; } @@ -233,6 +246,16 @@ class CAdminMod : public CModule { PutModule("DenyLoadMod = " + CString(pUser->DenyLoadMod())); else if (sVar == "denysetbindhost") PutModule("DenySetBindHost = " + CString(pUser->DenySetBindHost())); + else if (sVar == "denysetident") + PutModule("DenySetIdent = " + CString(pUser->DenySetIdent())); + else if (sVar == "denysetnetwork") + PutModule("DenySetNetwork = " + CString(pUser->DenySetNetwork())); + else if (sVar == "denysetrealname") + PutModule("DenySetRealName = " + CString(pUser->DenySetRealName())); + else if (sVar == "denysetquitmsg") + PutModule("DenySetQuitMsg = " + CString(pUser->DenySetQuitMsg())); + else if (sVar == "denysetctcpreplies") + PutModule("DenySetCTCPReplies = " + CString(pUser->DenySetCTCPReplies())); else if (sVar == "defaultchanmodes") PutModule("DefaultChanModes = " + pUser->GetDefaultChanModes()); else if (sVar == "quitmsg") @@ -273,6 +296,9 @@ class CAdminMod : public CModule { else if (sVar == "prependtimestamp") PutModule("PrependTimestamp = " + CString(pUser->GetTimestampPrepend())); + else if (sVar == "authonlyviamodule") + PutModule("AuthOnlyViaModule = " + + CString(pUser->AuthOnlyViaModule())); else if (sVar == "timestampformat") PutModule("TimestampFormat = " + pUser->GetTimestampFormat()); else if (sVar == "dccbindhost") @@ -297,7 +323,7 @@ class CAdminMod : public CModule { void Set(const CString& sLine) { const CString sVar = sLine.Token(1).AsLower(); - CString sUserName = sLine.Token(2); + CString sUsername = sLine.Token(2); CString sValue = sLine.Token(3, true); if (sValue.empty()) { @@ -305,7 +331,7 @@ class CAdminMod : public CModule { return; } - CUser* pUser = FindUser(sUserName); + CUser* pUser = FindUser(sUsername); if (!pUser) return; if (sVar == "nick") { @@ -315,11 +341,19 @@ class CAdminMod : public CModule { pUser->SetAltNick(sValue); PutModule("AltNick = " + sValue); } else if (sVar == "ident") { - pUser->SetIdent(sValue); - PutModule("Ident = " + sValue); + if (!pUser->DenySetIdent() || GetUser()->IsAdmin()) { + pUser->SetIdent(sValue); + PutModule("Ident = " + sValue); + } else { + PutModule(t_s("Access denied!")); + } } else if (sVar == "realname") { - pUser->SetRealName(sValue); - PutModule("RealName = " + sValue); + if (!pUser->DenySetRealName() || GetUser()->IsAdmin()) { + pUser->SetRealName(sValue); + PutModule("RealName = " + sValue); + } else { + PutModule(t_s("Access denied!")); + } } else if (sVar == "bindhost") { if (!pUser->DenySetBindHost() || GetUser()->IsAdmin()) { if (sValue.Equals(pUser->GetBindHost())) { @@ -352,12 +386,56 @@ class CAdminMod : public CModule { } else { PutModule(t_s("Access denied!")); } + } else if (sVar == "denysetident") { + if (GetUser()->IsAdmin()) { + bool b = sValue.ToBool(); + pUser->SetDenySetIdent(b); + PutModule("DenySetIdent = " + CString(b)); + } else { + PutModule(t_s("Access denied!")); + } + } else if (sVar == "denysetnetwork") { + if (GetUser()->IsAdmin()) { + bool b = sValue.ToBool(); + pUser->SetDenySetNetwork(b); + PutModule("DenySetNetwork = " + CString(b)); + } else { + PutModule(t_s("Access denied!")); + } + } else if (sVar == "denysetrealname") { + if (GetUser()->IsAdmin()) { + bool b = sValue.ToBool(); + pUser->SetDenySetRealName(b); + PutModule("DenySetRealName = " + CString(b)); + } else { + PutModule(t_s("Access denied!")); + } + } else if (sVar == "denysetquitmsg") { + if (GetUser()->IsAdmin()) { + bool b = sValue.ToBool(); + pUser->SetDenySetQuitMsg(b); + PutModule("DenySetQuitMsg = " + CString(b)); + } else { + PutModule(t_s("Access denied!")); + } + } else if (sVar == "denysetctcpreplies") { + if (GetUser()->IsAdmin()) { + bool b = sValue.ToBool(); + pUser->SetDenySetCTCPReplies(b); + PutModule("DenySetCTCPReplies = " + CString(b)); + } else { + PutModule(t_s("Access denied!")); + } } else if (sVar == "defaultchanmodes") { pUser->SetDefaultChanModes(sValue); PutModule("DefaultChanModes = " + sValue); } else if (sVar == "quitmsg") { - pUser->SetQuitMsg(sValue); - PutModule("QuitMsg = " + sValue); + if (!pUser->DenySetQuitMsg() || GetUser()->IsAdmin()) { + pUser->SetQuitMsg(sValue); + PutModule("QuitMsg = " + sValue); + } else { + PutModule(t_s("Access denied!")); + } } else if (sVar == "chanbuffersize" || sVar == "buffercount") { unsigned int i = sValue.ToUInt(); // Admins don't have to honour the buffer limit @@ -442,6 +520,14 @@ class CAdminMod : public CModule { bool b = sValue.ToBool(); pUser->SetTimestampAppend(b); PutModule("AppendTimestamp = " + CString(b)); + } else if (sVar == "authonlyviamodule") { + if (GetUser()->IsAdmin()) { + bool b = sValue.ToBool(); + pUser->SetAuthOnlyViaModule(b); + PutModule("AuthOnlyViaModule = " + CString(b)); + } else { + PutModule(t_s("Access denied!")); + } } else if (sVar == "timestampformat") { pUser->SetTimestampFormat(sValue); PutModule("TimestampFormat = " + sValue); @@ -483,7 +569,7 @@ class CAdminMod : public CModule { #ifdef HAVE_ICU else if (sVar == "clientencoding") { pUser->SetClientEncoding(sValue); - PutModule("ClientEncoding = " + sValue); + PutModule("ClientEncoding = " + pUser->GetClientEncoding()); } #endif else @@ -595,11 +681,19 @@ class CAdminMod : public CModule { pNetwork->SetAltNick(sValue); PutModule("AltNick = " + pNetwork->GetAltNick()); } else if (sVar.Equals("ident")) { - pNetwork->SetIdent(sValue); - PutModule("Ident = " + pNetwork->GetIdent()); + if (!pUser->DenySetIdent() || GetUser()->IsAdmin()) { + pNetwork->SetIdent(sValue); + PutModule("Ident = " + pNetwork->GetIdent()); + } else { + PutModule(t_s("Access denied!")); + } } else if (sVar.Equals("realname")) { - pNetwork->SetRealName(sValue); - PutModule("RealName = " + pNetwork->GetRealName()); + if (!pUser->DenySetRealName() || GetUser()->IsAdmin()) { + pNetwork->SetRealName(sValue); + PutModule("RealName = " + pNetwork->GetRealName()); + } else { + PutModule(t_s("Access denied!")); + } } else if (sVar.Equals("bindhost")) { if (!pUser->DenySetBindHost() || GetUser()->IsAdmin()) { if (sValue.Equals(pNetwork->GetBindHost())) { @@ -627,8 +721,12 @@ class CAdminMod : public CModule { PutModule("Encoding = " + pNetwork->GetEncoding()); #endif } else if (sVar.Equals("quitmsg")) { - pNetwork->SetQuitMsg(sValue); - PutModule("QuitMsg = " + pNetwork->GetQuitMsg()); + if (!pUser->DenySetQuitMsg() || GetUser()->IsAdmin()) { + pNetwork->SetQuitMsg(sValue); + PutModule("QuitMsg = " + pNetwork->GetQuitMsg()); + } else { + PutModule(t_s("Access denied!")); + } } else if (sVar.Equals("trustallcerts")) { bool b = sValue.ToBool(); pNetwork->SetTrustAllCerts(b); @@ -711,7 +809,7 @@ class CAdminMod : public CModule { } PutModule(t_p("Channel {1} is deleted from network {2} of user {3}", - "Channels {2} are deleted from network {2} of user {3}", + "Channels {1} are deleted from network {2} of user {3}", vsNames.size())( CString(", ").Join(vsNames.begin(), vsNames.end()), pNetwork->GetName(), sUsername)); @@ -955,7 +1053,7 @@ class CAdminMod : public CModule { return; } - if (!CZNC::Get().DeleteUser(pUser->GetUserName())) { + if (!CZNC::Get().DeleteUser(pUser->GetUsername())) { // This can't happen, because we got the user from FindUser() PutModule(t_s("Error: Internal error!")); return; @@ -1024,6 +1122,11 @@ class CAdminMod : public CModule { return; } + if (!GetUser()->IsAdmin() && pUser->DenySetNetwork()) { + PutModule(t_s("Access denied!")); + return; + } + if (!GetUser()->IsAdmin() && !pUser->HasSpaceForNewNetwork()) { PutStatus( t_s("Network number limit reached. Ask an admin to increase " @@ -1035,18 +1138,18 @@ class CAdminMod : public CModule { if (pUser->FindNetwork(sNetwork)) { PutModule( t_f("Error: User {1} already has a network with the name {2}")( - pUser->GetUserName(), sNetwork)); + pUser->GetUsername(), sNetwork)); return; } CString sNetworkAddError; if (pUser->AddNetwork(sNetwork, sNetworkAddError)) { PutModule(t_f("Network {1} added to user {2}.")( - sNetwork, pUser->GetUserName())); + sNetwork, pUser->GetUsername())); } else { PutModule(t_f( "Error: Network [{1}] could not be added for user {2}: {3}")( - sNetwork, pUser->GetUserName(), sNetworkAddError)); + sNetwork, pUser->GetUsername(), sNetworkAddError)); } } @@ -1069,6 +1172,11 @@ class CAdminMod : public CModule { return; } + if (!GetUser()->IsAdmin() && pUser->DenySetNetwork()) { + PutModule(t_s("Access denied!")); + return; + } + CIRCNetwork* pNetwork = FindNetwork(pUser, sNetwork); if (!pNetwork) { return; @@ -1083,11 +1191,11 @@ class CAdminMod : public CModule { if (pUser->DeleteNetwork(sNetwork)) { PutModule(t_f("Network {1} deleted for user {2}.")( - sNetwork, pUser->GetUserName())); + sNetwork, pUser->GetUsername())); } else { PutModule( t_f("Error: Network {1} could not be deleted for user {2}.")( - sNetwork, pUser->GetUserName())); + sNetwork, pUser->GetUsername())); } } @@ -1147,6 +1255,11 @@ class CAdminMod : public CModule { CUser* pUser = FindUser(sUsername); if (!pUser) return; + if (!GetUser()->IsAdmin() && pUser->DenySetNetwork()) { + PutModule(t_s("Access denied!")); + return; + } + CIRCNetwork* pNetwork = FindNetwork(pUser, sNetwork); if (!pNetwork) { return; @@ -1154,11 +1267,11 @@ class CAdminMod : public CModule { if (pNetwork->AddServer(sServer)) PutModule(t_f("Added IRC Server {1} to network {2} for user {3}.")( - sServer, pNetwork->GetName(), pUser->GetUserName())); + sServer, pNetwork->GetName(), pUser->GetUsername())); else PutModule(t_f( "Error: Could not add IRC server {1} to network {2} for user " - "{3}.")(sServer, pNetwork->GetName(), pUser->GetUserName())); + "{3}.")(sServer, pNetwork->GetName(), pUser->GetUsername())); } void DelServer(const CString& sLine) { @@ -1178,6 +1291,11 @@ class CAdminMod : public CModule { CUser* pUser = FindUser(sUsername); if (!pUser) return; + if (!GetUser()->IsAdmin() && pUser->DenySetNetwork()) { + PutModule(t_s("Access denied!")); + return; + } + CIRCNetwork* pNetwork = FindNetwork(pUser, sNetwork); if (!pNetwork) { return; @@ -1186,16 +1304,16 @@ class CAdminMod : public CModule { if (pNetwork->DelServer(sServer, uPort, sPass)) PutModule( t_f("Deleted IRC Server {1} from network {2} for user {3}.")( - sServer, pNetwork->GetName(), pUser->GetUserName())); + sServer, pNetwork->GetName(), pUser->GetUsername())); else PutModule( t_f("Error: Could not delete IRC server {1} from network {2} " "for user {3}.")(sServer, pNetwork->GetName(), - pUser->GetUserName())); + pUser->GetUsername())); } void ReconnectUser(const CString& sLine) { - CString sUserName = sLine.Token(1); + CString sUsername = sLine.Token(1); CString sNetwork = sLine.Token(2); if (sNetwork.empty()) { @@ -1203,7 +1321,7 @@ class CAdminMod : public CModule { return; } - CUser* pUser = FindUser(sUserName); + CUser* pUser = FindUser(sUsername); if (!pUser) { return; } @@ -1227,11 +1345,11 @@ class CAdminMod : public CModule { pNetwork->SetIRCConnectEnabled(true); PutModule(t_f("Queued network {1} of user {2} for a reconnect.")( - pNetwork->GetName(), pUser->GetUserName())); + pNetwork->GetName(), pUser->GetUsername())); } void DisconnectUser(const CString& sLine) { - CString sUserName = sLine.Token(1); + CString sUsername = sLine.Token(1); CString sNetwork = sLine.Token(2); if (sNetwork.empty()) { @@ -1239,7 +1357,7 @@ class CAdminMod : public CModule { return; } - CUser* pUser = FindUser(sUserName); + CUser* pUser = FindUser(sUsername); if (!pUser) { return; } @@ -1251,22 +1369,23 @@ class CAdminMod : public CModule { pNetwork->SetIRCConnectEnabled(false); PutModule(t_f("Closed IRC connection for network {1} of user {2}.")( - pNetwork->GetName(), pUser->GetUserName())); + pNetwork->GetName(), pUser->GetUsername())); } void ListCTCP(const CString& sLine) { - CString sUserName = sLine.Token(1, true); + CString sUsername = sLine.Token(1, true); - if (sUserName.empty()) { - sUserName = GetUser()->GetUserName(); + if (sUsername.empty()) { + sUsername = GetUser()->GetUsername(); } - CUser* pUser = FindUser(sUserName); + CUser* pUser = FindUser(sUsername); if (!pUser) return; const MCString& msCTCPReplies = pUser->GetCTCPReplies(); CTable Table; Table.AddColumn(t_s("Request", "listctcp")); Table.AddColumn(t_s("Reply", "listctcp")); + Table.SetStyle(CTable::ListStyle); for (const auto& it : msCTCPReplies) { Table.AddRow(); Table.SetCell(t_s("Request", "listctcp"), it.first); @@ -1275,22 +1394,22 @@ class CAdminMod : public CModule { if (Table.empty()) { PutModule(t_f("No CTCP replies for user {1} are configured")( - pUser->GetUserName())); + pUser->GetUsername())); } else { - PutModule(t_f("CTCP replies for user {1}:")(pUser->GetUserName())); + PutModule(t_f("CTCP replies for user {1}:")(pUser->GetUsername())); PutModule(Table); } } void AddCTCP(const CString& sLine) { - CString sUserName = sLine.Token(1); + CString sUsername = sLine.Token(1); CString sCTCPRequest = sLine.Token(2); CString sCTCPReply = sLine.Token(3, true); if (sCTCPRequest.empty()) { - sCTCPRequest = sUserName; + sCTCPRequest = sUsername; sCTCPReply = sLine.Token(2, true); - sUserName = GetUser()->GetUserName(); + sUsername = GetUser()->GetUsername(); } if (sCTCPRequest.empty()) { PutModule(t_s("Usage: AddCTCP [user] [request] [reply]")); @@ -1302,31 +1421,41 @@ class CAdminMod : public CModule { return; } - CUser* pUser = FindUser(sUserName); + CUser* pUser = FindUser(sUsername); if (!pUser) return; + if (!GetUser()->IsAdmin() && pUser->DenySetCTCPReplies()) { + PutModule(t_s("Access denied!")); + return; + } + pUser->AddCTCPReply(sCTCPRequest, sCTCPReply); if (sCTCPReply.empty()) { PutModule(t_f("CTCP requests {1} to user {2} will now be blocked.")( - sCTCPRequest.AsUpper(), pUser->GetUserName())); + sCTCPRequest.AsUpper(), pUser->GetUsername())); } else { PutModule( t_f("CTCP requests {1} to user {2} will now get reply: {3}")( - sCTCPRequest.AsUpper(), pUser->GetUserName(), sCTCPReply)); + sCTCPRequest.AsUpper(), pUser->GetUsername(), sCTCPReply)); } } void DelCTCP(const CString& sLine) { - CString sUserName = sLine.Token(1); + CString sUsername = sLine.Token(1); CString sCTCPRequest = sLine.Token(2, true); if (sCTCPRequest.empty()) { - sCTCPRequest = sUserName; - sUserName = GetUser()->GetUserName(); + sCTCPRequest = sUsername; + sUsername = GetUser()->GetUsername(); } - CUser* pUser = FindUser(sUserName); + CUser* pUser = FindUser(sUsername); if (!pUser) return; + if (!GetUser()->IsAdmin() && pUser->DenySetCTCPReplies()) { + PutModule(t_s("Access denied!")); + return; + } + if (sCTCPRequest.empty()) { PutModule(t_s("Usage: DelCTCP [user] [request]")); return; @@ -1335,12 +1464,12 @@ class CAdminMod : public CModule { if (pUser->DelCTCPReply(sCTCPRequest)) { PutModule(t_f( "CTCP requests {1} to user {2} will now be sent to IRC clients")( - sCTCPRequest.AsUpper(), pUser->GetUserName())); + sCTCPRequest.AsUpper(), pUser->GetUsername())); } else { PutModule( t_f("CTCP requests {1} to user {2} will be sent to IRC clients " "(nothing has changed)")(sCTCPRequest.AsUpper(), - pUser->GetUserName())); + pUser->GetUsername())); } } @@ -1481,6 +1610,7 @@ class CAdminMod : public CModule { CTable Table; Table.AddColumn(t_s("Name", "listmodules")); Table.AddColumn(t_s("Arguments", "listmodules")); + Table.SetStyle(CTable::ListStyle); for (const CModule* pMod : Modules) { Table.AddRow(); @@ -1504,11 +1634,11 @@ class CAdminMod : public CModule { if (pUser->GetModules().empty()) { PutModule( - t_f("User {1} has no modules loaded.")(pUser->GetUserName())); + t_f("User {1} has no modules loaded.")(pUser->GetUsername())); return; } - PutModule(t_f("Modules loaded for user {1}:")(pUser->GetUserName())); + PutModule(t_f("Modules loaded for user {1}:")(pUser->GetUsername())); ListModulesFor(pUser->GetModules()); } @@ -1528,12 +1658,13 @@ class CAdminMod : public CModule { if (!pNetwork) return; if (pNetwork->GetModules().empty()) { - PutModule(t_f("Network {1} of user {2} hasno modules loaded.")( - pNetwork->GetName(), pUser->GetUserName())); + PutModule(t_f("Network {1} of user {2} has no modules loaded.")( + pNetwork->GetName(), pUser->GetUsername())); + return; } PutModule(t_f("Modules loaded for network {1} of user {2}:")( - pNetwork->GetName(), pUser->GetUserName())); + pNetwork->GetName(), pUser->GetUsername())); ListModulesFor(pNetwork->GetModules()); } diff --git a/modules/corecaps.cpp b/modules/corecaps.cpp new file mode 100644 index 00000000..cefa535f --- /dev/null +++ b/modules/corecaps.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + +class CCoreCaps : public CModule { + // Note: for historical reasons CClient and CIRCSock have such fields, but + // really they should not. + // TODO: move these fields and their handling from core to this module. + class AwayNotify : public CCapability { + void OnServerChangedSupport(CIRCNetwork* pNetwork, + bool bState) override { + pNetwork->GetIRCSock()->m_bAwayNotify = bState; + } + void OnClientChangedSupport(CClient* pClient, bool bState) override { + pClient->m_bAwayNotify = bState; + } + }; + + class AccountNotify : public CCapability { + void OnServerChangedSupport(CIRCNetwork* pNetwork, + bool bState) override { + pNetwork->GetIRCSock()->m_bAccountNotify = bState; + } + void OnClientChangedSupport(CClient* pClient, bool bState) override { + pClient->m_bAccountNotify = bState; + } + }; + + class AccountTag : public CCapability { + void OnClientChangedSupport(CClient* pClient, bool bState) override { + pClient->SetTagSupport("account", bState); + } + }; + + class ExtendedJoin : public CCapability { + void OnServerChangedSupport(CIRCNetwork* pNetwork, + bool bState) override { + pNetwork->GetIRCSock()->m_bExtendedJoin = bState; + } + void OnClientChangedSupport(CClient* pClient, bool bState) override { + pClient->m_bExtendedJoin = bState; + } + }; + + public: + MODCONSTRUCTOR(CCoreCaps) { + AddServerDependentCapability("away-notify", std::make_unique()); + AddServerDependentCapability("account-notify", std::make_unique()); + AddServerDependentCapability("account-tag", std::make_unique()); + AddServerDependentCapability("extended-join", std::make_unique()); + } +}; + +GLOBALMODULEDEFS( + CCoreCaps, + t_s("Adds support for several IRC capabilities, extracted from ZNC core.")) diff --git a/modules/crypt.cpp b/modules/crypt.cpp index e342a139..94ec3be1 100644 --- a/modules/crypt.cpp +++ b/modules/crypt.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,8 +34,8 @@ // admin. // The keys are currently stored in plain text, so anyone with access to // your account (or root) can obtain them. -// It is strongly suggested that you enable SSL between znc and your -// client otherwise the encryption stops at znc and gets sent to your +// It is strongly suggested that you enable SSL between ZNC and your +// client otherwise the encryption stops at ZNC and gets sent to your // client in plain text. // @@ -58,7 +58,7 @@ class CCryptMod : public CModule { * ... all the way back to the original located at * http://mircryption.sourceforge.net/Extras/McpsFishDH.zip */ - const char* m_sPrime1080 = + static constexpr const char* m_sPrime1080 = "FBE1022E23D213E8ACFA9AE8B9DFADA3EA6B7AC7A7B7E95AB5EB2DF858921FEADE95E6" "AC7BE7DE6ADBAB8A783E7AF7A7FA6A2B7BEB1E72EAE2B72F9FA2BFB2A2EFBEFAC868BA" "DB3E828FA8BADFADA3E4CC1BE7E8AFE85E9698A783EB68FA07A77AB6AD7BEB618ACF9C" @@ -68,7 +68,8 @@ class CCryptMod : public CModule { CString m_sPrivKey; CString m_sPubKey; -#if OPENSSL_VERSION_NUMBER < 0X10100000L +#if OPENSSL_VERSION_NUMBER < 0X10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL) static int DH_set0_pqg(DH* dh, BIGNUM* p, BIGNUM* q, BIGNUM* g) { /* If the fields p and g in dh are nullptr, the corresponding input * parameters MUST be non-nullptr. q may remain nullptr. @@ -142,7 +143,7 @@ class CCryptMod : public CModule { } bool DH1080_comp(CString& sOtherPubKey, CString& sSecretKey) { - unsigned long len; + long len; unsigned char* key = nullptr; BIGNUM* bOtherPubKey = nullptr; @@ -228,35 +229,23 @@ class CCryptMod : public CModule { return true; } - EModRet OnUserMsg(CString& sTarget, CString& sMessage) override { - return FilterOutgoing(sTarget, sMessage, "PRIVMSG", "", ""); + EModRet OnUserTextMessage(CTextMessage& Message) override { + FilterOutgoing(Message); + return CONTINUE; } - EModRet OnUserNotice(CString& sTarget, CString& sMessage) override { - return FilterOutgoing(sTarget, sMessage, "NOTICE", "", ""); + EModRet OnUserNoticeMessage(CNoticeMessage& Message) override { + FilterOutgoing(Message); + return CONTINUE; } - EModRet OnUserAction(CString& sTarget, CString& sMessage) override { - return FilterOutgoing(sTarget, sMessage, "PRIVMSG", "\001ACTION ", - "\001"); + EModRet OnUserActionMessage(CActionMessage& Message) override { + FilterOutgoing(Message); + return CONTINUE; } - EModRet OnUserTopic(CString& sTarget, CString& sMessage) override { - sTarget.TrimPrefix(NickPrefix()); - - if (sMessage.TrimPrefix("``")) { - return CONTINUE; - } - - MCString::iterator it = FindNV(sTarget.AsLower()); - - if (it != EndNV()) { - sMessage = MakeIvec() + sMessage; - sMessage.Encrypt(it->second); - sMessage.Base64Encode(); - sMessage = "+OK *" + sMessage; - } - + EModRet OnUserTopicMessage(CTopicMessage& Message) override { + FilterOutgoing(Message); return CONTINUE; } @@ -346,62 +335,42 @@ class CCryptMod : public CModule { return CONTINUE; } - EModRet OnRaw(CString& sLine) override { - if (!sLine.Token(1).Equals("332")) { + EModRet OnNumericMessage(CNumericMessage& Message) override { + if (Message.GetCode() != 332) { return CONTINUE; } - CChan* pChan = GetNetwork()->FindChan(sLine.Token(3)); + CChan* pChan = GetNetwork()->FindChan(Message.GetParam(1)); if (pChan) { - CNick* Nick = pChan->FindNick(sLine.Token(2)); - CString sTopic = sLine.Token(4, true); - sTopic.TrimPrefix(":"); + CNick* Nick = pChan->FindNick(Message.GetParam(0)); + CString sTopic = Message.GetParam(2); FilterIncoming(pChan->GetName(), *Nick, sTopic); - sLine = sLine.Token(0) + " " + sLine.Token(1) + " " + - sLine.Token(2) + " " + pChan->GetName() + " :" + sTopic; + Message.SetParam(2, sTopic); } return CONTINUE; } - EModRet FilterOutgoing(CString& sTarget, CString& sMessage, - const CString& sType, const CString& sPreMsg, - const CString& sPostMsg) { + template + void FilterOutgoing(T& Msg) { + CString sTarget = Msg.GetTarget(); sTarget.TrimPrefix(NickPrefix()); + Msg.SetTarget(sTarget); + + CString sMessage = Msg.GetText(); if (sMessage.TrimPrefix("``")) { - return CONTINUE; + return; } MCString::iterator it = FindNV(sTarget.AsLower()); - if (it != EndNV()) { - CChan* pChan = GetNetwork()->FindChan(sTarget); - CString sNickMask = GetNetwork()->GetIRCNick().GetNickMask(); - if (pChan) { - if (!pChan->AutoClearChanBuffer()) - pChan->AddBuffer(":" + NickPrefix() + _NAMEDFMT(sNickMask) + - " " + sType + " " + - _NAMEDFMT(sTarget) + " :" + sPreMsg + - "{text}" + sPostMsg, - sMessage); - GetUser()->PutUser(":" + NickPrefix() + sNickMask + " " + - sType + " " + sTarget + " :" + sPreMsg + - sMessage + sPostMsg, - nullptr, GetClient()); - } - - CString sMsg = MakeIvec() + sMessage; - sMsg.Encrypt(it->second); - sMsg.Base64Encode(); - sMsg = "+OK *" + sMsg; - - PutIRC(sType + " " + sTarget + " :" + sPreMsg + sMsg + sPostMsg); - return HALTCORE; + sMessage = MakeIvec() + sMessage; + sMessage.Encrypt(it->second); + sMessage.Base64Encode(); + Msg.SetText("+OK *" + sMessage); } - - return CONTINUE; } void FilterIncoming(const CString& sTarget, CNick& Nick, @@ -505,6 +474,7 @@ class CCryptMod : public CModule { CTable Table; Table.AddColumn(t_s("Target", "listkeys")); Table.AddColumn(t_s("Key", "listkeys")); + Table.SetStyle(CTable::ListStyle); for (MCString::iterator it = BeginNV(); it != EndNV(); ++it) { if (!it->first.Equals(NICK_PREFIX_KEY)) { diff --git a/modules/ctcpflood.cpp b/modules/ctcpflood.cpp index f9affc31..1af27200 100644 --- a/modules/ctcpflood.cpp +++ b/modules/ctcpflood.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/modules/cyrusauth.cpp b/modules/cyrusauth.cpp index 36eb18bc..3d0bcd81 100644 --- a/modules/cyrusauth.cpp +++ b/modules/cyrusauth.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details. + * Copyright (C) 2004-2025 ZNC, see the NOTICE file for details. * Copyright (C) 2008 Heiko Hund * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/modules/data/cert/tmpl/index.tmpl b/modules/data/cert/tmpl/index.tmpl index 332339c4..d3a12cee 100644 --- a/modules/data/cert/tmpl/index.tmpl +++ b/modules/data/cert/tmpl/index.tmpl @@ -2,8 +2,8 @@ - -

+ +

diff --git a/modules/data/webadmin/files/webadmin.css b/modules/data/webadmin/files/webadmin.css index 3fa3fa84..8a2b02e6 100644 --- a/modules/data/webadmin/files/webadmin.css +++ b/modules/data/webadmin/files/webadmin.css @@ -21,3 +21,4 @@ table .reverse-sorted::after { width: 400px; } +table.networks[data-network-edit="0"] .network_delete { display: none; } \ No newline at end of file diff --git a/modules/data/webadmin/files/webadmin.js b/modules/data/webadmin/files/webadmin.js index ba2ff687..0fb776be 100644 --- a/modules/data/webadmin/files/webadmin.js +++ b/modules/data/webadmin/files/webadmin.js @@ -121,18 +121,31 @@ function serverlist_init($) { row.remove(); serialize(); } - row.append( - $("").append($("").attr({"type":"text"}) - .addClass("servers_row_host").val(host)), - $("").append($("").attr({"type":"number"}) - .addClass("servers_row_port").val(port)), - $("").append($("").attr({"type":"checkbox"}) - .addClass("servers_row_ssl").prop("checked", ssl)), - $("").append($("").attr({"type":"text"}) - .addClass("servers_row_pass").val(pass)), - $("").append($("").attr({"type":"button"}) - .val("X").click(delete_row)) - ); + if (NetworkEdit) { + row.append( + $("").append($("").attr({"type":"text"}) + .addClass("servers_row_host").val(host)), + $("").append($("").attr({"type":"number"}) + .addClass("servers_row_port").val(port)), + $("").append($("").attr({"type":"checkbox"}) + .addClass("servers_row_ssl").prop("checked", ssl)), + $("").append($("").attr({"type":"text"}) + .addClass("servers_row_pass").val(pass)), + $("").append($("").attr({"type":"button"}) + .val("X").click(delete_row)) + ); + } else { + row.append( + $("").append($("").attr({"type":"text","disabled":true}) + .addClass("servers_row_host").val(host)), + $("").append($("").attr({"type":"number","disabled":true}) + .addClass("servers_row_port").val(port)), + $("").append($("").attr({"type":"checkbox","disabled":true}) + .addClass("servers_row_ssl").prop("checked", ssl)), + $("").append($("").attr({"type":"text","disabled":true}) + .addClass("servers_row_pass").val(pass)) + ); + } $("input", row).change(serialize); $("#servers_tbody").append(row); } @@ -165,6 +178,31 @@ function serverlist_init($) { })(); } +function channellist_init($) { + function update_rows() { + $("#channels > tr").each(function(i) { + $(this).toggleClass("evenrow", i % 2 === 1).toggleClass("oddrow", i % 2 === 0); + $(this).find(".channel_index").val(i + 1); + }); + } + $("#channels").sortable({ + axis: "y", + update: update_rows + }); + $(".channel_index").change(function() { + var src = $(this).closest("tr").detach(); + var rows = $("#channels > tr"); + var dst = rows[this.value - 1]; + + if (dst) + src.insertBefore(dst); + else + src.insertAfter(rows.last()); + + update_rows(); + }); +} + function ctcpreplies_init($) { function serialize() { var text = ""; @@ -185,16 +223,27 @@ function ctcpreplies_init($) { row.remove(); serialize(); } - row.append( - $("").append($("").val(request) - .addClass("ctcpreplies_row_request") - .attr({"type":"text","list":"ctcpreplies_list"})), - $("").append($("").val(response) - .addClass("ctcpreplies_row_response") - .attr({"type":"text","placeholder":$("#ctcpreplies_js").data("placeholder")})), - $("").append($("").val("X") - .attr({"type":"button"}).click(delete_row)) - ); + if (CTCPEdit) { + row.append( + $("").append($("").val(request) + .addClass("ctcpreplies_row_request") + .attr({"type":"text","list":"ctcpreplies_list"})), + $("").append($("").val(response) + .addClass("ctcpreplies_row_response") + .attr({"type":"text","placeholder":$("#ctcpreplies_js").data("placeholder")})), + $("").append($("").val("X") + .attr({"type":"button"}).click(delete_row)) + ); + } else { + row.append( + $("").append($("").val(request) + .addClass("ctcpreplies_row_request") + .attr({"type":"text","list":"ctcpreplies_list","disabled":true})), + $("").append($("").val(response) + .addClass("ctcpreplies_row_response") + .attr({"type":"text","placeholder":$("#ctcpreplies_js").data("placeholder"),"disabled":true})), + ); + } $("input", row).change(serialize); $("#ctcpreplies_tbody").append(row); } diff --git a/modules/data/webadmin/tmpl/add_edit_chan.tmpl b/modules/data/webadmin/tmpl/add_edit_chan.tmpl index 1d030d0b..4446a4ee 100644 --- a/modules/data/webadmin/tmpl/add_edit_chan.tmpl +++ b/modules/data/webadmin/tmpl/add_edit_chan.tmpl @@ -27,13 +27,13 @@
-
+
"/>
-
+
"/>
diff --git a/modules/data/webadmin/tmpl/add_edit_network.tmpl b/modules/data/webadmin/tmpl/add_edit_network.tmpl index a1d05f7a..a0239711 100644 --- a/modules/data/webadmin/tmpl/add_edit_network.tmpl +++ b/modules/data/webadmin/tmpl/add_edit_network.tmpl @@ -3,6 +3,10 @@ + +
/: @@ -11,7 +15,7 @@ /: / -

{1} or username field as {2}" "ClientConnectHint_Password ESC=" "ClientConnectHint_Username ESC=" ?> +

{1} or username field as {2}" "ClientConnectHint_Password ESC=" "ClientConnectHint_Username ESC=" ?>

@@ -27,8 +31,9 @@
- "/> + " disabled /> +
@@ -45,12 +50,12 @@
"/> + title="" disabled />
"/> + title="" disabled />
@@ -62,9 +67,10 @@
-
+
"/> + title="" + disabled />
@@ -80,14 +86,14 @@
-
+
checked="checked" /> -
+
-

@@ -102,12 +108,16 @@ + + + +
@@ -184,6 +194,7 @@ [] + @@ -196,13 +207,14 @@ - - + + [] [] + checked="checked" /> @@ -213,6 +225,7 @@ + @@ -249,7 +262,7 @@ - + @@ -258,7 +271,7 @@ disabled="disabled"/> - + diff --git a/modules/data/webadmin/tmpl/add_edit_user.tmpl b/modules/data/webadmin/tmpl/add_edit_user.tmpl index b6c08c86..20256bb8 100644 --- a/modules/data/webadmin/tmpl/add_edit_user.tmpl +++ b/modules/data/webadmin/tmpl/add_edit_user.tmpl @@ -3,6 +3,10 @@ + +
@@ -40,6 +44,12 @@ "/>
+
+
+ " + checked="checked" disabled="disabled" /> +

TIME Buy a watch!" ?>